ETH Price: $2,486.87 (+1.68%)

Transaction Decoder

Block:
20882500 at Oct-03-2024 04:08:23 AM +UTC
Transaction Fee:
0.00059976 ETH $1.49
Gas Used:
149,940 Gas / 4 Gwei

Emitted Events:

735 RocketTokenRETH.EtherDeposited( from=[Receiver] RocketNodeDistributor, amount=50172998218153803, time=1727928503 )
736 RocketNodeDistributor.0x4c41dd034da8150bccdeba2e484837eb447e0a3840b3e02a54e9bd6eb883210e( 0x4c41dd034da8150bccdeba2e484837eb447e0a3840b3e02a54e9bd6eb883210e, 00000000000000000000000018845fd4aa047d6d43c934cc37aca94f85def683, 00000000000000000000000000000000000000000000000000b24013781bff4b, 00000000000000000000000000000000000000000000000000621b542456d9e7, 0000000000000000000000000000000000000000000000000000000066fe18b7 )

Account State Difference:

  Address   Before After State Difference Code
0x18845fD4...f85dEf683
0.186001252853295549 Eth
Nonce: 146
0.185401492853295549 Eth
Nonce: 147
0.00059976
0x5eA25ecF...deB849074
(Fee Recipient: 0x5ea2...074)
0.077787594136672562 Eth0 Eth0.077787594136672562
(beaverbuild)
9.432151444513164955 Eth9.432162677623622755 Eth0.0000112331104578
0xae78736C...E74Fc6393 4,268.831949465583361748 Eth4,268.882122463801515551 Eth0.050172998218153803
0xB006C9F5...0788f2873 0.416554119597835816 Eth0.444168715516354575 Eth0.027614595918518759

Execution Trace

RocketNodeDistributor.CALL( )
  • RocketStorage.getAddress( _key=FB3483C04A4C870B23F9315CE407ACEFF6EE8DD58EB34AA5BECFB781351D3FB8 ) => ( r=0x32778D6bf5b93B89177D328556EeeB35c09f472b )
  • RocketNodeDistributorDelegate.DELEGATECALL( )
    • RocketStorage.getAddress( _key=AF00BE55C9FB8F543C04E0AA0D70351B880C1BFAFFFD15B60065A4A50C85EC94 ) => ( r=0x2b52479F6ea009907e46fc43e91064D1b92Fdc86 )
    • RocketStorage.getAddress( _key=8FC06385DE84508EAF7EB3D75B93167987C9629589FE0A868A2B4E0E90862DD8 ) => ( r=0x0e29BA1155cE103A07118c8912dA44B0507A982D )
    • RocketNodeManager.getAverageNodeFee( _nodeAddress=0x18845fD4AA047D6d43C934cC37aca94f85dEf683 ) => ( 140000000000000000 )
      • RocketStorage.getAddress( _key=E9DFEC9339B94A131861A58F1BB4AC4C1CE55C7FFE8550E0B6EBCFDE87BB012F ) => ( r=0x09fbCE43e4021a3F69C4599FF00362b83edA501E )
      • RocketStorage.getAddress( _key=C5F0E8E643416573963C05884A77DFC5EBA3461EB8D39B60A0A58AEDCA955FA5 ) => ( r=0x9304B4ebFbE68932Cf9Af8De4d21D7e7621f701a )
      • RocketStorage.getAddress( _key=CABD9A4F404C4FFB362A3C40647A1A12F3BDAA2AD3BBCDB92EA716C2EA3FF22B ) => ( r=0xA416A7a07925d60F794E20532bc730749611A220 )
      • RocketNodeDeposit.STATICCALL( )
      • RocketDAOProtocolSettingsMinipool.STATICCALL( )
      • RocketMinipoolManager.getNodeStakingMinipoolCountBySize( _nodeAddress=0x18845fD4AA047D6d43C934cC37aca94f85dEf683, _depositSize=16000000000000000000 ) => ( 0 )
        • RocketStorage.getUint( _key=1285FEECC9591786BF7B470830B2D10296C66441A2AFBD0890D8B6831D045C6C ) => ( r=0 )
        • RocketMinipoolManager.getNodeStakingMinipoolCountBySize( _nodeAddress=0x18845fD4AA047D6d43C934cC37aca94f85dEf683, _depositSize=8000000000000000000 ) => ( 7 )
          • RocketStorage.getUint( _key=7E27ACAA289B64E63B7092215DDF07967699568A75FCCC64F2B9A544B022B0ED ) => ( r=7 )
          • RocketStorage.getUint( _key=4434975C1D6A43117581E327460F44038A6948DB44181ED44D3CB99936AA5702 ) => ( r=980000000000000000 )
          • RocketNodeStaking.getNodeETHCollateralisationRatio( _nodeAddress=0x18845fD4AA047D6d43C934cC37aca94f85dEf683 ) => ( 4000000000000000000 )
            • RocketStorage.getAddress( _key=EF27338BD03B62FAA0EAA677490D4A98A821E044D70D76339D384AA33E96C86B ) => ( r=0x7603352f1C4752Ac07AAC94e48632b65FDF1D35c )
            • RocketNetworkSnapshots.latest( _key=E5A41A7B82A3B81F58818E38B6792BD746F59E1C8E5F849E33B706562530F6AB ) => ( True, 20112634, 168000000000000000000 )
              • RocketStorage.getUint( _key=E8631B5D762B8407D9E3552761D63BB8F5BA2AC29B679FF5D6577E658A71E241 ) => ( r=1 )
              • RocketStorage.getBytes32( _key=E5A41A7B82A3B81F58818E38B6792BD746F59E1C8E5F849E33B706562530F6AB ) => ( r=0132E4FA00000000000000000000000000000000000000091B77E5E5D9A00000 )
              • RocketStorage.getAddress( _key=CABD9A4F404C4FFB362A3C40647A1A12F3BDAA2AD3BBCDB92EA716C2EA3FF22B ) => ( r=0xA416A7a07925d60F794E20532bc730749611A220 )
              • RocketDAOProtocolSettingsMinipool.STATICCALL( )
              • RocketStorage.getAddress( _key=E9DFEC9339B94A131861A58F1BB4AC4C1CE55C7FFE8550E0B6EBCFDE87BB012F ) => ( r=0x09fbCE43e4021a3F69C4599FF00362b83edA501E )
              • RocketMinipoolManager.getNodeActiveMinipoolCount( _nodeAddress=0x18845fD4AA047D6d43C934cC37aca94f85dEf683 ) => ( 7 )
                • RocketStorage.getAddress( _key=EF27338BD03B62FAA0EAA677490D4A98A821E044D70D76339D384AA33E96C86B ) => ( r=0x7603352f1C4752Ac07AAC94e48632b65FDF1D35c )
                • RocketNetworkSnapshots.latest( _key=282A78D28747EF84F4FABAD38ED9AE9BCF07F1D351E0CAFFBBF33BF40BEB9739 ) => ( True, 20112634, 7 )
                  • RocketStorage.getUint( _key=97383B38F414996DFE7CB79E07829922C8ABD829DE994191714EA8B5CDEE258B ) => ( r=1 )
                  • RocketStorage.getBytes32( _key=282A78D28747EF84F4FABAD38ED9AE9BCF07F1D351E0CAFFBBF33BF40BEB9739 ) => ( r=0132E4FA00000000000000000000000000000000000000000000000000000007 )
                  • RocketStorage.getNodeWithdrawalAddress( _nodeAddress=0x18845fD4AA047D6d43C934cC37aca94f85dEf683 ) => ( 0xB006C9F5AB30670721F9Fb22F0071d90788f2873 )
                  • ETH 0.027614595918518759 0xb006c9f5ab30670721f9fb22f0071d90788f2873.CALL( )
                  • RocketStorage.getAddress( _key=E3744443225BFF7CC22028BE036B80DE58057D65A3FDCA0A3DF329F525E31CCC ) => ( r=0xae78736Cd615f374D3085123A210448E74Fc6393 )
                  • ETH 0.050172998218153803 RocketTokenRETH.CALL( )
                    File 1 of 10: RocketNodeDistributor
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    /// @title Base settings / modifiers for each contract in Rocket Pool
                    /// @author David Rugendyke
                    abstract contract RocketBase {
                        // Calculate using this as the base
                        uint256 constant calcBase = 1 ether;
                        // Version of the contract
                        uint8 public version;
                        // The main storage contract where primary persistant storage is maintained
                        RocketStorageInterface rocketStorage = RocketStorageInterface(0);
                        /*** Modifiers **********************************************************/
                        /**
                        * @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
                        */
                        modifier onlyLatestNetworkContract() {
                            require(getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))), "Invalid or outdated network contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that doesn't match one of the supplied contract or is the latest version of that contract
                        */
                        modifier onlyLatestContract(string memory _contractName, address _contractAddress) {
                            require(_contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", _contractName))), "Invalid or outdated contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered node
                        */
                        modifier onlyRegisteredNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a trusted node DAO member
                        */
                        modifier onlyTrustedNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("dao.trustednodes.", "member", _nodeAddress))), "Invalid trusted node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered minipool
                        */
                        modifier onlyRegisteredMinipool(address _minipoolAddress) {
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress))), "Invalid minipool");
                            _;
                        }
                        
                        /**
                        * @dev Throws if called by any account other than a guardian account (temporary account allowed access to settings before DAO is fully enabled)
                        */
                        modifier onlyGuardian() {
                            require(msg.sender == rocketStorage.getGuardian(), "Account is not a temporary guardian");
                            _;
                        }
                        /*** Methods **********************************************************/
                        /// @dev Set the main Rocket Storage address
                        constructor(RocketStorageInterface _rocketStorageAddress) {
                            // Update the contract address
                            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
                        }
                        /// @dev Get the address of a network contract by name
                        function getContractAddress(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Check it
                            require(contractAddress != address(0x0), "Contract not found");
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)
                        function getContractAddressUnsafe(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the name of a network contract by address
                        function getContractName(address _contractAddress) internal view returns (string memory) {
                            // Get the contract name
                            string memory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
                            // Check it
                            require(bytes(contractName).length > 0, "Contract not found");
                            // Return
                            return contractName;
                        }
                        /// @dev Get revert error message from a .call method
                        function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return "Transaction reverted silently";
                            assembly {
                                // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string)); // All that remains is the revert string
                        }
                        /*** Rocket Storage Methods ****************************************/
                        // Note: Unused helpers have been removed to keep contract sizes down
                        /// @dev Storage get methods
                        function getAddress(bytes32 _key) internal view returns (address) { return rocketStorage.getAddress(_key); }
                        function getUint(bytes32 _key) internal view returns (uint) { return rocketStorage.getUint(_key); }
                        function getString(bytes32 _key) internal view returns (string memory) { return rocketStorage.getString(_key); }
                        function getBytes(bytes32 _key) internal view returns (bytes memory) { return rocketStorage.getBytes(_key); }
                        function getBool(bytes32 _key) internal view returns (bool) { return rocketStorage.getBool(_key); }
                        function getInt(bytes32 _key) internal view returns (int) { return rocketStorage.getInt(_key); }
                        function getBytes32(bytes32 _key) internal view returns (bytes32) { return rocketStorage.getBytes32(_key); }
                        /// @dev Storage set methods
                        function setAddress(bytes32 _key, address _value) internal { rocketStorage.setAddress(_key, _value); }
                        function setUint(bytes32 _key, uint _value) internal { rocketStorage.setUint(_key, _value); }
                        function setString(bytes32 _key, string memory _value) internal { rocketStorage.setString(_key, _value); }
                        function setBytes(bytes32 _key, bytes memory _value) internal { rocketStorage.setBytes(_key, _value); }
                        function setBool(bytes32 _key, bool _value) internal { rocketStorage.setBool(_key, _value); }
                        function setInt(bytes32 _key, int _value) internal { rocketStorage.setInt(_key, _value); }
                        function setBytes32(bytes32 _key, bytes32 _value) internal { rocketStorage.setBytes32(_key, _value); }
                        /// @dev Storage delete methods
                        function deleteAddress(bytes32 _key) internal { rocketStorage.deleteAddress(_key); }
                        function deleteUint(bytes32 _key) internal { rocketStorage.deleteUint(_key); }
                        function deleteString(bytes32 _key) internal { rocketStorage.deleteString(_key); }
                        function deleteBytes(bytes32 _key) internal { rocketStorage.deleteBytes(_key); }
                        function deleteBool(bytes32 _key) internal { rocketStorage.deleteBool(_key); }
                        function deleteInt(bytes32 _key) internal { rocketStorage.deleteInt(_key); }
                        function deleteBytes32(bytes32 _key) internal { rocketStorage.deleteBytes32(_key); }
                        /// @dev Storage arithmetic methods
                        function addUint(bytes32 _key, uint256 _amount) internal { rocketStorage.addUint(_key, _amount); }
                        function subUint(bytes32 _key, uint256 _amount) internal { rocketStorage.subUint(_key, _amount); }
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../RocketBase.sol";
                    import "./RocketNodeDistributorStorageLayout.sol";
                    contract RocketNodeDistributor is RocketNodeDistributorStorageLayout {
                        bytes32 immutable distributorStorageKey;
                        constructor(address _nodeAddress, address _rocketStorage) {
                            rocketStorage = RocketStorageInterface(_rocketStorage);
                            nodeAddress = _nodeAddress;
                            // Precompute storage key for rocketNodeDistributorDelegate
                            distributorStorageKey = keccak256(abi.encodePacked("contract.address", "rocketNodeDistributorDelegate"));
                        }
                        // Allow contract to receive ETH without making a delegated call
                        receive() external payable {}
                        // Delegates all transactions to the target supplied during creation
                        fallback() external payable {
                            address _target = rocketStorage.getAddress(distributorStorageKey);
                            assembly {
                                calldatacopy(0x0, 0x0, calldatasize())
                                let result := delegatecall(gas(), _target, 0x0, calldatasize(), 0x0, 0)
                                returndatacopy(0x0, 0x0, returndatasize())
                                switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())}
                            }
                        }
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    import "../../interface/RocketStorageInterface.sol";
                    // SPDX-License-Identifier: GPL-3.0-only
                    abstract contract RocketNodeDistributorStorageLayout {
                        RocketStorageInterface rocketStorage;
                        address nodeAddress;
                    }/**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    

                    File 2 of 10: RocketTokenRETH
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.0 <0.8.0;
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            uint256 c = a + b;
                            if (c < a) return (false, 0);
                            return (true, c);
                        }
                        /**
                         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b > a) return (false, 0);
                            return (true, a - b);
                        }
                        /**
                         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                            if (a == 0) return (true, 0);
                            uint256 c = a * b;
                            if (c / a != b) return (false, 0);
                            return (true, c);
                        }
                        /**
                         * @dev Returns the division of two unsigned integers, with a division by zero flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b == 0) return (false, 0);
                            return (true, a / b);
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b == 0) return (false, 0);
                            return (true, a % b);
                        }
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         *
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                            return c;
                        }
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         *
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a, "SafeMath: subtraction overflow");
                            return a - b;
                        }
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         *
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            if (a == 0) return 0;
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                            return c;
                        }
                        /**
                         * @dev Returns the integer division of two unsigned integers, reverting on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0, "SafeMath: division by zero");
                            return a / b;
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * reverting when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0, "SafeMath: modulo by zero");
                            return a % b;
                        }
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                         * overflow (when the result is negative).
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {trySub}.
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         *
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b <= a, errorMessage);
                            return a - b;
                        }
                        /**
                         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                         * division by zero. The result is rounded towards zero.
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {tryDiv}.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b > 0, errorMessage);
                            return a / b;
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * reverting with custom message when dividing by zero.
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {tryMod}.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b > 0, errorMessage);
                            return a % b;
                        }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.0 <0.8.0;
                    import "../../utils/Context.sol";
                    import "./IERC20.sol";
                    import "../../math/SafeMath.sol";
                    /**
                     * @dev Implementation of the {IERC20} interface.
                     *
                     * This implementation is agnostic to the way tokens are created. This means
                     * that a supply mechanism has to be added in a derived contract using {_mint}.
                     * For a generic mechanism see {ERC20PresetMinterPauser}.
                     *
                     * TIP: For a detailed writeup see our guide
                     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                     * to implement supply mechanisms].
                     *
                     * We have followed general OpenZeppelin guidelines: functions revert instead
                     * of returning `false` on failure. This behavior is nonetheless conventional
                     * and does not conflict with the expectations of ERC20 applications.
                     *
                     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                     * This allows applications to reconstruct the allowance for all accounts just
                     * by listening to said events. Other implementations of the EIP may not emit
                     * these events, as it isn't required by the specification.
                     *
                     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                     * functions have been added to mitigate the well-known issues around setting
                     * allowances. See {IERC20-approve}.
                     */
                    contract ERC20 is Context, IERC20 {
                        using SafeMath for uint256;
                        mapping (address => uint256) private _balances;
                        mapping (address => mapping (address => uint256)) private _allowances;
                        uint256 private _totalSupply;
                        string private _name;
                        string private _symbol;
                        uint8 private _decimals;
                        /**
                         * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
                         * a default value of 18.
                         *
                         * To select a different value for {decimals}, use {_setupDecimals}.
                         *
                         * All three of these values are immutable: they can only be set once during
                         * construction.
                         */
                        constructor (string memory name_, string memory symbol_) public {
                            _name = name_;
                            _symbol = symbol_;
                            _decimals = 18;
                        }
                        /**
                         * @dev Returns the name of the token.
                         */
                        function name() public view virtual returns (string memory) {
                            return _name;
                        }
                        /**
                         * @dev Returns the symbol of the token, usually a shorter version of the
                         * name.
                         */
                        function symbol() public view virtual returns (string memory) {
                            return _symbol;
                        }
                        /**
                         * @dev Returns the number of decimals used to get its user representation.
                         * For example, if `decimals` equals `2`, a balance of `505` tokens should
                         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
                         *
                         * Tokens usually opt for a value of 18, imitating the relationship between
                         * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
                         * called.
                         *
                         * NOTE: This information is only used for _display_ purposes: it in
                         * no way affects any of the arithmetic of the contract, including
                         * {IERC20-balanceOf} and {IERC20-transfer}.
                         */
                        function decimals() public view virtual returns (uint8) {
                            return _decimals;
                        }
                        /**
                         * @dev See {IERC20-totalSupply}.
                         */
                        function totalSupply() public view virtual override returns (uint256) {
                            return _totalSupply;
                        }
                        /**
                         * @dev See {IERC20-balanceOf}.
                         */
                        function balanceOf(address account) public view virtual override returns (uint256) {
                            return _balances[account];
                        }
                        /**
                         * @dev See {IERC20-transfer}.
                         *
                         * Requirements:
                         *
                         * - `recipient` cannot be the zero address.
                         * - the caller must have a balance of at least `amount`.
                         */
                        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                            _transfer(_msgSender(), recipient, amount);
                            return true;
                        }
                        /**
                         * @dev See {IERC20-allowance}.
                         */
                        function allowance(address owner, address spender) public view virtual override returns (uint256) {
                            return _allowances[owner][spender];
                        }
                        /**
                         * @dev See {IERC20-approve}.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function approve(address spender, uint256 amount) public virtual override returns (bool) {
                            _approve(_msgSender(), spender, amount);
                            return true;
                        }
                        /**
                         * @dev See {IERC20-transferFrom}.
                         *
                         * Emits an {Approval} event indicating the updated allowance. This is not
                         * required by the EIP. See the note at the beginning of {ERC20}.
                         *
                         * Requirements:
                         *
                         * - `sender` and `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         * - the caller must have allowance for ``sender``'s tokens of at least
                         * `amount`.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                            _transfer(sender, recipient, amount);
                            _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                            return true;
                        }
                        /**
                         * @dev Atomically increases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                            return true;
                        }
                        /**
                         * @dev Atomically decreases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `spender` must have allowance for the caller of at least
                         * `subtractedValue`.
                         */
                        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                            return true;
                        }
                        /**
                         * @dev Moves tokens `amount` from `sender` to `recipient`.
                         *
                         * This is internal function is equivalent to {transfer}, and can be used to
                         * e.g. implement automatic token fees, slashing mechanisms, etc.
                         *
                         * Emits a {Transfer} event.
                         *
                         * Requirements:
                         *
                         * - `sender` cannot be the zero address.
                         * - `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         */
                        function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                            require(sender != address(0), "ERC20: transfer from the zero address");
                            require(recipient != address(0), "ERC20: transfer to the zero address");
                            _beforeTokenTransfer(sender, recipient, amount);
                            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                            _balances[recipient] = _balances[recipient].add(amount);
                            emit Transfer(sender, recipient, amount);
                        }
                        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                         * the total supply.
                         *
                         * Emits a {Transfer} event with `from` set to the zero address.
                         *
                         * Requirements:
                         *
                         * - `to` cannot be the zero address.
                         */
                        function _mint(address account, uint256 amount) internal virtual {
                            require(account != address(0), "ERC20: mint to the zero address");
                            _beforeTokenTransfer(address(0), account, amount);
                            _totalSupply = _totalSupply.add(amount);
                            _balances[account] = _balances[account].add(amount);
                            emit Transfer(address(0), account, amount);
                        }
                        /**
                         * @dev Destroys `amount` tokens from `account`, reducing the
                         * total supply.
                         *
                         * Emits a {Transfer} event with `to` set to the zero address.
                         *
                         * Requirements:
                         *
                         * - `account` cannot be the zero address.
                         * - `account` must have at least `amount` tokens.
                         */
                        function _burn(address account, uint256 amount) internal virtual {
                            require(account != address(0), "ERC20: burn from the zero address");
                            _beforeTokenTransfer(account, address(0), amount);
                            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                            _totalSupply = _totalSupply.sub(amount);
                            emit Transfer(account, address(0), amount);
                        }
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                         *
                         * This internal function is equivalent to `approve`, and can be used to
                         * e.g. set automatic allowances for certain subsystems, etc.
                         *
                         * Emits an {Approval} event.
                         *
                         * Requirements:
                         *
                         * - `owner` cannot be the zero address.
                         * - `spender` cannot be the zero address.
                         */
                        function _approve(address owner, address spender, uint256 amount) internal virtual {
                            require(owner != address(0), "ERC20: approve from the zero address");
                            require(spender != address(0), "ERC20: approve to the zero address");
                            _allowances[owner][spender] = amount;
                            emit Approval(owner, spender, amount);
                        }
                        /**
                         * @dev Sets {decimals} to a value other than the default one of 18.
                         *
                         * WARNING: This function should only be called from the constructor. Most
                         * applications that interact with token contracts will not expect
                         * {decimals} to ever change, and may work incorrectly if it does.
                         */
                        function _setupDecimals(uint8 decimals_) internal virtual {
                            _decimals = decimals_;
                        }
                        /**
                         * @dev Hook that is called before any transfer of tokens. This includes
                         * minting and burning.
                         *
                         * Calling conditions:
                         *
                         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                         * will be to transferred to `to`.
                         * - when `from` is zero, `amount` tokens will be minted for `to`.
                         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                         * - `from` and `to` are never both zero.
                         *
                         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                         */
                        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.0 <0.8.0;
                    /**
                     * @dev Interface of the ERC20 standard as defined in the EIP.
                     */
                    interface IERC20 {
                        /**
                         * @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 `recipient`.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
                        /**
                         * @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);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.0 <0.8.0;
                    /*
                     * @dev Provides information about the current execution context, including the
                     * sender of the transaction and its data. While these are generally available
                     * via msg.sender and msg.data, they should not be accessed in such a direct
                     * manner, since when dealing with GSN meta-transactions the account sending and
                     * paying for execution may not be the actual sender (as far as an application
                     * is concerned).
                     *
                     * This contract is only required for intermediate, library-like contracts.
                     */
                    abstract contract Context {
                        function _msgSender() internal view virtual returns (address payable) {
                            return msg.sender;
                        }
                        function _msgData() internal view virtual returns (bytes memory) {
                            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                            return msg.data;
                        }
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    /// @title Base settings / modifiers for each contract in Rocket Pool
                    /// @author David Rugendyke
                    abstract contract RocketBase {
                        // Calculate using this as the base
                        uint256 constant calcBase = 1 ether;
                        // Version of the contract
                        uint8 public version;
                        // The main storage contract where primary persistant storage is maintained
                        RocketStorageInterface rocketStorage = RocketStorageInterface(0);
                        /*** Modifiers **********************************************************/
                        /**
                        * @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
                        */
                        modifier onlyLatestNetworkContract() {
                            require(getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))), "Invalid or outdated network contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that doesn't match one of the supplied contract or is the latest version of that contract
                        */
                        modifier onlyLatestContract(string memory _contractName, address _contractAddress) {
                            require(_contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", _contractName))), "Invalid or outdated contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered node
                        */
                        modifier onlyRegisteredNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a trusted node DAO member
                        */
                        modifier onlyTrustedNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("dao.trustednodes.", "member", _nodeAddress))), "Invalid trusted node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered minipool
                        */
                        modifier onlyRegisteredMinipool(address _minipoolAddress) {
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress))), "Invalid minipool");
                            _;
                        }
                        
                        /**
                        * @dev Throws if called by any account other than a guardian account (temporary account allowed access to settings before DAO is fully enabled)
                        */
                        modifier onlyGuardian() {
                            require(msg.sender == rocketStorage.getGuardian(), "Account is not a temporary guardian");
                            _;
                        }
                        /*** Methods **********************************************************/
                        /// @dev Set the main Rocket Storage address
                        constructor(RocketStorageInterface _rocketStorageAddress) {
                            // Update the contract address
                            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
                        }
                        /// @dev Get the address of a network contract by name
                        function getContractAddress(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Check it
                            require(contractAddress != address(0x0), "Contract not found");
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)
                        function getContractAddressUnsafe(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the name of a network contract by address
                        function getContractName(address _contractAddress) internal view returns (string memory) {
                            // Get the contract name
                            string memory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
                            // Check it
                            require(bytes(contractName).length > 0, "Contract not found");
                            // Return
                            return contractName;
                        }
                        /// @dev Get revert error message from a .call method
                        function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return "Transaction reverted silently";
                            assembly {
                                // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string)); // All that remains is the revert string
                        }
                        /*** Rocket Storage Methods ****************************************/
                        // Note: Unused helpers have been removed to keep contract sizes down
                        /// @dev Storage get methods
                        function getAddress(bytes32 _key) internal view returns (address) { return rocketStorage.getAddress(_key); }
                        function getUint(bytes32 _key) internal view returns (uint) { return rocketStorage.getUint(_key); }
                        function getString(bytes32 _key) internal view returns (string memory) { return rocketStorage.getString(_key); }
                        function getBytes(bytes32 _key) internal view returns (bytes memory) { return rocketStorage.getBytes(_key); }
                        function getBool(bytes32 _key) internal view returns (bool) { return rocketStorage.getBool(_key); }
                        function getInt(bytes32 _key) internal view returns (int) { return rocketStorage.getInt(_key); }
                        function getBytes32(bytes32 _key) internal view returns (bytes32) { return rocketStorage.getBytes32(_key); }
                        /// @dev Storage set methods
                        function setAddress(bytes32 _key, address _value) internal { rocketStorage.setAddress(_key, _value); }
                        function setUint(bytes32 _key, uint _value) internal { rocketStorage.setUint(_key, _value); }
                        function setString(bytes32 _key, string memory _value) internal { rocketStorage.setString(_key, _value); }
                        function setBytes(bytes32 _key, bytes memory _value) internal { rocketStorage.setBytes(_key, _value); }
                        function setBool(bytes32 _key, bool _value) internal { rocketStorage.setBool(_key, _value); }
                        function setInt(bytes32 _key, int _value) internal { rocketStorage.setInt(_key, _value); }
                        function setBytes32(bytes32 _key, bytes32 _value) internal { rocketStorage.setBytes32(_key, _value); }
                        /// @dev Storage delete methods
                        function deleteAddress(bytes32 _key) internal { rocketStorage.deleteAddress(_key); }
                        function deleteUint(bytes32 _key) internal { rocketStorage.deleteUint(_key); }
                        function deleteString(bytes32 _key) internal { rocketStorage.deleteString(_key); }
                        function deleteBytes(bytes32 _key) internal { rocketStorage.deleteBytes(_key); }
                        function deleteBool(bytes32 _key) internal { rocketStorage.deleteBool(_key); }
                        function deleteInt(bytes32 _key) internal { rocketStorage.deleteInt(_key); }
                        function deleteBytes32(bytes32 _key) internal { rocketStorage.deleteBytes32(_key); }
                        /// @dev Storage arithmetic methods
                        function addUint(bytes32 _key, uint256 _amount) internal { rocketStorage.addUint(_key, _amount); }
                        function subUint(bytes32 _key, uint256 _amount) internal { rocketStorage.subUint(_key, _amount); }
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
                    import "../RocketBase.sol";
                    import "../../interface/deposit/RocketDepositPoolInterface.sol";
                    import "../../interface/network/RocketNetworkBalancesInterface.sol";
                    import "../../interface/token/RocketTokenRETHInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNetworkInterface.sol";
                    // rETH is a tokenised stake in the Rocket Pool network
                    // rETH is backed by ETH (subject to liquidity) at a variable exchange rate
                    contract RocketTokenRETH is RocketBase, ERC20, RocketTokenRETHInterface {
                        // Libs
                        using SafeMath for uint;
                        // Events
                        event EtherDeposited(address indexed from, uint256 amount, uint256 time);
                        event TokensMinted(address indexed to, uint256 amount, uint256 ethAmount, uint256 time);
                        event TokensBurned(address indexed from, uint256 amount, uint256 ethAmount, uint256 time);
                        // Construct with our token details
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) ERC20("Rocket Pool ETH", "rETH") {
                            // Version
                            version = 1;
                        }
                        // Receive an ETH deposit from a minipool or generous individual
                        receive() external payable {
                            // Emit ether deposited event
                            emit EtherDeposited(msg.sender, msg.value, block.timestamp);
                        }
                        // Calculate the amount of ETH backing an amount of rETH
                        function getEthValue(uint256 _rethAmount) override public view returns (uint256) {
                            // Get network balances
                            RocketNetworkBalancesInterface rocketNetworkBalances = RocketNetworkBalancesInterface(getContractAddress("rocketNetworkBalances"));
                            uint256 totalEthBalance = rocketNetworkBalances.getTotalETHBalance();
                            uint256 rethSupply = rocketNetworkBalances.getTotalRETHSupply();
                            // Use 1:1 ratio if no rETH is minted
                            if (rethSupply == 0) { return _rethAmount; }
                            // Calculate and return
                            return _rethAmount.mul(totalEthBalance).div(rethSupply);
                        }
                        // Calculate the amount of rETH backed by an amount of ETH
                        function getRethValue(uint256 _ethAmount) override public view returns (uint256) {
                            // Get network balances
                            RocketNetworkBalancesInterface rocketNetworkBalances = RocketNetworkBalancesInterface(getContractAddress("rocketNetworkBalances"));
                            uint256 totalEthBalance = rocketNetworkBalances.getTotalETHBalance();
                            uint256 rethSupply = rocketNetworkBalances.getTotalRETHSupply();
                            // Use 1:1 ratio if no rETH is minted
                            if (rethSupply == 0) { return _ethAmount; }
                            // Check network ETH balance
                            require(totalEthBalance > 0, "Cannot calculate rETH token amount while total network balance is zero");
                            // Calculate and return
                            return _ethAmount.mul(rethSupply).div(totalEthBalance);
                        }
                        // Get the current ETH : rETH exchange rate
                        // Returns the amount of ETH backing 1 rETH
                        function getExchangeRate() override external view returns (uint256) {
                            return getEthValue(1 ether);
                        }
                        // Get the total amount of collateral available
                        // Includes rETH contract balance & excess deposit pool balance
                        function getTotalCollateral() override public view returns (uint256) {
                            RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
                            return rocketDepositPool.getExcessBalance().add(address(this).balance);
                        }
                        // Get the current ETH collateral rate
                        // Returns the portion of rETH backed by ETH in the contract as a fraction of 1 ether
                        function getCollateralRate() override public view returns (uint256) {
                            uint256 totalEthValue = getEthValue(totalSupply());
                            if (totalEthValue == 0) { return calcBase; }
                            return calcBase.mul(address(this).balance).div(totalEthValue);
                        }
                        // Deposit excess ETH from deposit pool
                        // Only accepts calls from the RocketDepositPool contract
                        function depositExcess() override external payable onlyLatestContract("rocketDepositPool", msg.sender) {
                            // Emit ether deposited event
                            emit EtherDeposited(msg.sender, msg.value, block.timestamp);
                        }
                        // Mint rETH
                        // Only accepts calls from the RocketDepositPool contract
                        function mint(uint256 _ethAmount, address _to) override external onlyLatestContract("rocketDepositPool", msg.sender) {
                            // Get rETH amount
                            uint256 rethAmount = getRethValue(_ethAmount);
                            // Check rETH amount
                            require(rethAmount > 0, "Invalid token mint amount");
                            // Update balance & supply
                            _mint(_to, rethAmount);
                            // Emit tokens minted event
                            emit TokensMinted(_to, rethAmount, _ethAmount, block.timestamp);
                        }
                        // Burn rETH for ETH
                        function burn(uint256 _rethAmount) override external {
                            // Check rETH amount
                            require(_rethAmount > 0, "Invalid token burn amount");
                            require(balanceOf(msg.sender) >= _rethAmount, "Insufficient rETH balance");
                            // Get ETH amount
                            uint256 ethAmount = getEthValue(_rethAmount);
                            // Get & check ETH balance
                            uint256 ethBalance = getTotalCollateral();
                            require(ethBalance >= ethAmount, "Insufficient ETH balance for exchange");
                            // Update balance & supply
                            _burn(msg.sender, _rethAmount);
                            // Withdraw ETH from deposit pool if required
                            withdrawDepositCollateral(ethAmount);
                            // Transfer ETH to sender
                            msg.sender.transfer(ethAmount);
                            // Emit tokens burned event
                            emit TokensBurned(msg.sender, _rethAmount, ethAmount, block.timestamp);
                        }
                        // Withdraw ETH from the deposit pool for collateral if required
                        function withdrawDepositCollateral(uint256 _ethRequired) private {
                            // Check rETH contract balance
                            uint256 ethBalance = address(this).balance;
                            if (ethBalance >= _ethRequired) { return; }
                            // Withdraw
                            RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
                            rocketDepositPool.withdrawExcessBalance(_ethRequired.sub(ethBalance));
                        }
                        // Sends any excess ETH from this contract to the deposit pool (as determined by target collateral rate)
                        function depositExcessCollateral() external override {
                            // Load contracts
                            RocketDAOProtocolSettingsNetworkInterface rocketDAOProtocolSettingsNetwork = RocketDAOProtocolSettingsNetworkInterface(getContractAddress("rocketDAOProtocolSettingsNetwork"));
                            RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
                            // Get collateral and target collateral rate
                            uint256 collateralRate = getCollateralRate();
                            uint256 targetCollateralRate = rocketDAOProtocolSettingsNetwork.getTargetRethCollateralRate();
                            // Check if we are in excess
                            if (collateralRate > targetCollateralRate) {
                                // Calculate our target collateral in ETH
                                uint256 targetCollateral = address(this).balance.mul(targetCollateralRate).div(collateralRate);
                                // If we have excess
                                if (address(this).balance > targetCollateral) {
                                    // Send that excess to deposit pool
                                    uint256 excessCollateral = address(this).balance.sub(targetCollateral);
                                    rocketDepositPool.recycleExcessCollateral{value: excessCollateral}();
                                }
                            }
                        }
                        // This is called by the base ERC20 contract before all transfer, mint, and burns
                        function _beforeTokenTransfer(address from, address, uint256) internal override {
                            // Don't run check if this is a mint transaction
                            if (from != address(0)) {
                                // Check which block the user's last deposit was
                                bytes32 key = keccak256(abi.encodePacked("user.deposit.block", from));
                                uint256 lastDepositBlock = getUint(key);
                                if (lastDepositBlock > 0) {
                                    // Ensure enough blocks have passed
                                    uint256 depositDelay = getUint(keccak256(abi.encodePacked(keccak256("dao.protocol.setting.network"), "network.reth.deposit.delay")));
                                    uint256 blocksPassed = block.number.sub(lastDepositBlock);
                                    require(blocksPassed > depositDelay, "Not enough time has passed since deposit");
                                    // Clear the state as it's no longer necessary to check this until another deposit is made
                                    deleteUint(key);
                                }
                            }
                        }
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAOProtocolSettingsNetworkInterface {
                        function getNodeConsensusThreshold() external view returns (uint256);
                        function getSubmitBalancesEnabled() external view returns (bool);
                        function getSubmitBalancesFrequency() external view returns (uint256);
                        function getSubmitPricesEnabled() external view returns (bool);
                        function getSubmitPricesFrequency() external view returns (uint256);
                        function getMinimumNodeFee() external view returns (uint256);
                        function getTargetNodeFee() external view returns (uint256);
                        function getMaximumNodeFee() external view returns (uint256);
                        function getNodeFeeDemandRange() external view returns (uint256);
                        function getTargetRethCollateralRate() external view returns (uint256);
                        function getRethDepositDelay() external view returns (uint256);
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDepositPoolInterface {
                        function getBalance() external view returns (uint256);
                        function getExcessBalance() external view returns (uint256);
                        function deposit() external payable;
                        function recycleDissolvedDeposit() external payable;
                        function recycleExcessCollateral() external payable;
                        function recycleLiquidatedStake() external payable;
                        function assignDeposits() external;
                        function withdrawExcessBalance(uint256 _amount) external;
                        function getUserLastDepositBlock(address _address) external view returns (uint256);
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNetworkBalancesInterface {
                        function getBalancesBlock() external view returns (uint256);
                        function getLatestReportableBlock() external view returns (uint256);
                        function getTotalETHBalance() external view returns (uint256);
                        function getStakingETHBalance() external view returns (uint256);
                        function getTotalRETHSupply() external view returns (uint256);
                        function getETHUtilizationRate() external view returns (uint256);
                        function submitBalances(uint256 _block, uint256 _total, uint256 _staking, uint256 _rethSupply) external;
                        function executeUpdateBalances(uint256 _block, uint256 _totalEth, uint256 _stakingEth, uint256 _rethSupply) external;
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    interface RocketTokenRETHInterface is IERC20 {
                        function getEthValue(uint256 _rethAmount) external view returns (uint256);
                        function getRethValue(uint256 _ethAmount) external view returns (uint256);
                        function getExchangeRate() external view returns (uint256);
                        function getTotalCollateral() external view returns (uint256);
                        function getCollateralRate() external view returns (uint256);
                        function depositExcess() external payable;
                        function depositExcessCollateral() external;
                        function mint(uint256 _ethAmount, address _to) external;
                        function burn(uint256 _rethAmount) external;
                    }
                    

                    File 3 of 10: RocketStorage
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.0 <0.8.0;
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            uint256 c = a + b;
                            if (c < a) return (false, 0);
                            return (true, c);
                        }
                        /**
                         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b > a) return (false, 0);
                            return (true, a - b);
                        }
                        /**
                         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                            if (a == 0) return (true, 0);
                            uint256 c = a * b;
                            if (c / a != b) return (false, 0);
                            return (true, c);
                        }
                        /**
                         * @dev Returns the division of two unsigned integers, with a division by zero flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b == 0) return (false, 0);
                            return (true, a / b);
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b == 0) return (false, 0);
                            return (true, a % b);
                        }
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         *
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                            return c;
                        }
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         *
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a, "SafeMath: subtraction overflow");
                            return a - b;
                        }
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         *
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            if (a == 0) return 0;
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                            return c;
                        }
                        /**
                         * @dev Returns the integer division of two unsigned integers, reverting on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0, "SafeMath: division by zero");
                            return a / b;
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * reverting when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0, "SafeMath: modulo by zero");
                            return a % b;
                        }
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                         * overflow (when the result is negative).
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {trySub}.
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         *
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b <= a, errorMessage);
                            return a - b;
                        }
                        /**
                         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                         * division by zero. The result is rounded towards zero.
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {tryDiv}.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b > 0, errorMessage);
                            return a / b;
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * reverting with custom message when dividing by zero.
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {tryMod}.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b > 0, errorMessage);
                            return a % b;
                        }
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    import "@openzeppelin/contracts/math/SafeMath.sol";
                    /// @title The primary persistent storage for Rocket Pool
                    /// @author David Rugendyke
                    contract RocketStorage is RocketStorageInterface {
                        // Events
                        event NodeWithdrawalAddressSet(address indexed node, address indexed withdrawalAddress, uint256 time);
                        event GuardianChanged(address oldGuardian, address newGuardian);
                        // Libraries
                        using SafeMath for uint256;
                        // Storage maps
                        mapping(bytes32 => string)     private stringStorage;
                        mapping(bytes32 => bytes)      private bytesStorage;
                        mapping(bytes32 => uint256)    private uintStorage;
                        mapping(bytes32 => int256)     private intStorage;
                        mapping(bytes32 => address)    private addressStorage;
                        mapping(bytes32 => bool)       private booleanStorage;
                        mapping(bytes32 => bytes32)    private bytes32Storage;
                        // Protected storage (not accessible by network contracts)
                        mapping(address => address)    private withdrawalAddresses;
                        mapping(address => address)    private pendingWithdrawalAddresses;
                        // Guardian address
                        address guardian;
                        address newGuardian;
                        // Flag storage has been initialised
                        bool storageInit = false;
                        /// @dev Only allow access from the latest version of a contract in the Rocket Pool network after deployment
                        modifier onlyLatestRocketNetworkContract() {
                            if (storageInit == true) {
                                // Make sure the access is permitted to only contracts in our Dapp
                                require(booleanStorage[keccak256(abi.encodePacked("contract.exists", msg.sender))], "Invalid or outdated network contract");
                            } else {
                                // Only Dapp and the guardian account are allowed access during initialisation.
                                // tx.origin is only safe to use in this case for deployment since no external contracts are interacted with
                                require((
                                    booleanStorage[keccak256(abi.encodePacked("contract.exists", msg.sender))] || tx.origin == guardian
                                ), "Invalid or outdated network contract attempting access during deployment");
                            }
                            _;
                        }
                        /// @dev Construct RocketStorage
                        constructor() {
                            // Set the guardian upon deployment
                            guardian = msg.sender;
                        }
                        // Get guardian address
                        function getGuardian() external override view returns (address) {
                            return guardian;
                        }
                        // Transfers guardianship to a new address
                        function setGuardian(address _newAddress) external override {
                            // Check tx comes from current guardian
                            require(msg.sender == guardian, "Is not guardian account");
                            // Store new address awaiting confirmation
                            newGuardian = _newAddress;
                        }
                        // Confirms change of guardian
                        function confirmGuardian() external override {
                            // Check tx came from new guardian address
                            require(msg.sender == newGuardian, "Confirmation must come from new guardian address");
                            // Store old guardian for event
                            address oldGuardian = guardian;
                            // Update guardian and clear storage
                            guardian = newGuardian;
                            delete newGuardian;
                            // Emit event
                            emit GuardianChanged(oldGuardian, guardian);
                        }
                        // Set this as being deployed now
                        function getDeployedStatus() external override view returns (bool) {
                            return storageInit;
                        }
                        // Set this as being deployed now
                        function setDeployedStatus() external {
                            // Only guardian can lock this down
                            require(msg.sender == guardian, "Is not guardian account");
                            // Set it now
                            storageInit = true;
                        }
                        // Protected storage
                        // Get a node's withdrawal address
                        function getNodeWithdrawalAddress(address _nodeAddress) public override view returns (address) {
                            // If no withdrawal address has been set, return the nodes address
                            address withdrawalAddress = withdrawalAddresses[_nodeAddress];
                            if (withdrawalAddress == address(0)) {
                                return _nodeAddress;
                            }
                            return withdrawalAddress;
                        }
                        // Get a node's pending withdrawal address
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external override view returns (address) {
                            return pendingWithdrawalAddresses[_nodeAddress];
                        }
                        // Set a node's withdrawal address
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external override {
                            // Check new withdrawal address
                            require(_newWithdrawalAddress != address(0x0), "Invalid withdrawal address");
                            // Confirm the transaction is from the node's current withdrawal address
                            address withdrawalAddress = getNodeWithdrawalAddress(_nodeAddress);
                            require(withdrawalAddress == msg.sender, "Only a tx from a node's withdrawal address can update it");
                            // Update immediately if confirmed
                            if (_confirm) {
                                updateWithdrawalAddress(_nodeAddress, _newWithdrawalAddress);
                            }
                            // Set pending withdrawal address if not confirmed
                            else {
                                pendingWithdrawalAddresses[_nodeAddress] = _newWithdrawalAddress;
                            }
                        }
                        // Confirm a node's new withdrawal address
                        function confirmWithdrawalAddress(address _nodeAddress) external override {
                            // Get node by pending withdrawal address
                            require(pendingWithdrawalAddresses[_nodeAddress] == msg.sender, "Confirmation must come from the pending withdrawal address");
                            delete pendingWithdrawalAddresses[_nodeAddress];
                            // Update withdrawal address
                            updateWithdrawalAddress(_nodeAddress, msg.sender);
                        }
                        // Update a node's withdrawal address
                        function updateWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress) private {
                            // Set new withdrawal address
                            withdrawalAddresses[_nodeAddress] = _newWithdrawalAddress;
                            // Emit withdrawal address set event
                            emit NodeWithdrawalAddressSet(_nodeAddress, _newWithdrawalAddress, block.timestamp);
                        }
                        /// @param _key The key for the record
                        function getAddress(bytes32 _key) override external view returns (address r) {
                            return addressStorage[_key];
                        }
                        /// @param _key The key for the record
                        function getUint(bytes32 _key) override external view returns (uint256 r) {
                            return uintStorage[_key];
                        }
                        /// @param _key The key for the record
                        function getString(bytes32 _key) override external view returns (string memory) {
                            return stringStorage[_key];
                        }
                        /// @param _key The key for the record
                        function getBytes(bytes32 _key) override external view returns (bytes memory) {
                            return bytesStorage[_key];
                        }
                        /// @param _key The key for the record
                        function getBool(bytes32 _key) override external view returns (bool r) {
                            return booleanStorage[_key];
                        }
                        /// @param _key The key for the record
                        function getInt(bytes32 _key) override external view returns (int r) {
                            return intStorage[_key];
                        }
                        /// @param _key The key for the record
                        function getBytes32(bytes32 _key) override external view returns (bytes32 r) {
                            return bytes32Storage[_key];
                        }
                        /// @param _key The key for the record
                        function setAddress(bytes32 _key, address _value) onlyLatestRocketNetworkContract override external {
                            addressStorage[_key] = _value;
                        }
                        /// @param _key The key for the record
                        function setUint(bytes32 _key, uint _value) onlyLatestRocketNetworkContract override external {
                            uintStorage[_key] = _value;
                        }
                        /// @param _key The key for the record
                        function setString(bytes32 _key, string calldata _value) onlyLatestRocketNetworkContract override external {
                            stringStorage[_key] = _value;
                        }
                        /// @param _key The key for the record
                        function setBytes(bytes32 _key, bytes calldata _value) onlyLatestRocketNetworkContract override external {
                            bytesStorage[_key] = _value;
                        }
                        /// @param _key The key for the record
                        function setBool(bytes32 _key, bool _value) onlyLatestRocketNetworkContract override external {
                            booleanStorage[_key] = _value;
                        }
                        /// @param _key The key for the record
                        function setInt(bytes32 _key, int _value) onlyLatestRocketNetworkContract override external {
                            intStorage[_key] = _value;
                        }
                        /// @param _key The key for the record
                        function setBytes32(bytes32 _key, bytes32 _value) onlyLatestRocketNetworkContract override external {
                            bytes32Storage[_key] = _value;
                        }
                        /// @param _key The key for the record
                        function deleteAddress(bytes32 _key) onlyLatestRocketNetworkContract override external {
                            delete addressStorage[_key];
                        }
                        /// @param _key The key for the record
                        function deleteUint(bytes32 _key) onlyLatestRocketNetworkContract override external {
                            delete uintStorage[_key];
                        }
                        /// @param _key The key for the record
                        function deleteString(bytes32 _key) onlyLatestRocketNetworkContract override external {
                            delete stringStorage[_key];
                        }
                        /// @param _key The key for the record
                        function deleteBytes(bytes32 _key) onlyLatestRocketNetworkContract override external {
                            delete bytesStorage[_key];
                        }
                        /// @param _key The key for the record
                        function deleteBool(bytes32 _key) onlyLatestRocketNetworkContract override external {
                            delete booleanStorage[_key];
                        }
                        /// @param _key The key for the record
                        function deleteInt(bytes32 _key) onlyLatestRocketNetworkContract override external {
                            delete intStorage[_key];
                        }
                        /// @param _key The key for the record
                        function deleteBytes32(bytes32 _key) onlyLatestRocketNetworkContract override external {
                            delete bytes32Storage[_key];
                        }
                        /// @param _key The key for the record
                        /// @param _amount An amount to add to the record's value
                        function addUint(bytes32 _key, uint256 _amount) onlyLatestRocketNetworkContract override external {
                            uintStorage[_key] = uintStorage[_key].add(_amount);
                        }
                        /// @param _key The key for the record
                        /// @param _amount An amount to subtract from the record's value
                        function subUint(bytes32 _key, uint256 _amount) onlyLatestRocketNetworkContract override external {
                            uintStorage[_key] = uintStorage[_key].sub(_amount);
                        }
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                      *  be community-owned, decentralised, and trustless.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    

                    File 4 of 10: RocketNodeDistributorDelegate
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.0 <0.8.0;
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            uint256 c = a + b;
                            if (c < a) return (false, 0);
                            return (true, c);
                        }
                        /**
                         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b > a) return (false, 0);
                            return (true, a - b);
                        }
                        /**
                         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                            if (a == 0) return (true, 0);
                            uint256 c = a * b;
                            if (c / a != b) return (false, 0);
                            return (true, c);
                        }
                        /**
                         * @dev Returns the division of two unsigned integers, with a division by zero flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b == 0) return (false, 0);
                            return (true, a / b);
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b == 0) return (false, 0);
                            return (true, a % b);
                        }
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         *
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                            return c;
                        }
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         *
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a, "SafeMath: subtraction overflow");
                            return a - b;
                        }
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         *
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            if (a == 0) return 0;
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                            return c;
                        }
                        /**
                         * @dev Returns the integer division of two unsigned integers, reverting on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0, "SafeMath: division by zero");
                            return a / b;
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * reverting when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0, "SafeMath: modulo by zero");
                            return a % b;
                        }
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                         * overflow (when the result is negative).
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {trySub}.
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         *
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b <= a, errorMessage);
                            return a - b;
                        }
                        /**
                         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                         * division by zero. The result is rounded towards zero.
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {tryDiv}.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b > 0, errorMessage);
                            return a / b;
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * reverting with custom message when dividing by zero.
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {tryMod}.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b > 0, errorMessage);
                            return a % b;
                        }
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    import "../../interface/RocketStorageInterface.sol";
                    // SPDX-License-Identifier: GPL-3.0-only
                    abstract contract RocketNodeDistributorStorageLayout {
                        RocketStorageInterface rocketStorage;
                        address nodeAddress;
                        uint256 lock;   // Reentrancy guard
                    }/**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // A struct containing all the information on-chain about a specific node
                    struct NodeDetails {
                        bool exists;
                        uint256 registrationTime;
                        string timezoneLocation;
                        bool feeDistributorInitialised;
                        address feeDistributorAddress;
                        uint256 rewardNetwork;
                        uint256 rplStake;
                        uint256 effectiveRPLStake;
                        uint256 minimumRPLStake;
                        uint256 maximumRPLStake;
                        uint256 ethMatched;
                        uint256 ethMatchedLimit;
                        uint256 minipoolCount;
                        uint256 balanceETH;
                        uint256 balanceRETH;
                        uint256 balanceRPL;
                        uint256 balanceOldRPL;
                        uint256 depositCreditBalance;
                        uint256 distributorBalanceUserETH;
                        uint256 distributorBalanceNodeETH;
                        address withdrawalAddress;
                        address pendingWithdrawalAddress;
                        bool smoothingPoolRegistrationState;
                        uint256 smoothingPoolRegistrationChanged;
                        address nodeAddress;
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/NodeDetails.sol";
                    interface RocketNodeManagerInterface {
                        // Structs
                        struct TimezoneCount {
                            string timezone;
                            uint256 count;
                        }
                        function getNodeCount() external view returns (uint256);
                        function getNodeCountPerTimezone(uint256 offset, uint256 limit) external view returns (TimezoneCount[] memory);
                        function getNodeAt(uint256 _index) external view returns (address);
                        function getNodeExists(address _nodeAddress) external view returns (bool);
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeTimezoneLocation(address _nodeAddress) external view returns (string memory);
                        function registerNode(string calldata _timezoneLocation) external;
                        function getNodeRegistrationTime(address _nodeAddress) external view returns (uint256);
                        function setTimezoneLocation(string calldata _timezoneLocation) external;
                        function setRewardNetwork(address _nodeAddress, uint256 network) external;
                        function getRewardNetwork(address _nodeAddress) external view returns (uint256);
                        function getFeeDistributorInitialised(address _nodeAddress) external view returns (bool);
                        function initialiseFeeDistributor() external;
                        function getAverageNodeFee(address _nodeAddress) external view returns (uint256);
                        function setSmoothingPoolRegistrationState(bool _state) external;
                        function getSmoothingPoolRegistrationState(address _nodeAddress) external returns (bool);
                        function getSmoothingPoolRegistrationChanged(address _nodeAddress) external returns (uint256);
                        function getSmoothingPoolRegisteredNodeCount(uint256 _offset, uint256 _limit) external view returns (uint256);
                        function getNodeDetails(address _nodeAddress) external view returns (NodeDetails memory);
                        function getNodeAddresses(uint256 _offset, uint256 _limit) external view returns (address[] memory);
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity 0.7.6;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNodeDistributorInterface {
                        function getNodeShare() external view returns (uint256);
                        function getUserShare() external view returns (uint256);
                        function distribute() external;
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNodeStakingInterface {
                        function getTotalRPLStake() external view returns (uint256);
                        function getNodeRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatched(address _nodeAddress) external view returns (uint256);
                        function getNodeETHProvided(address _nodeAddress) external view returns (uint256);
                        function getNodeETHCollateralisationRatio(address _nodeAddress) external view returns (uint256);
                        function getNodeRPLStakedTime(address _nodeAddress) external view returns (uint256);
                        function getNodeEffectiveRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMinimumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMaximumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatchedLimit(address _nodeAddress) external view returns (uint256);
                        function stakeRPL(uint256 _amount) external;
                        function stakeRPLFor(address _nodeAddress, uint256 _amount) external;
                        function setStakeRPLForAllowed(address _caller, bool _allowed) external;
                        function withdrawRPL(uint256 _amount) external;
                        function slashRPL(address _nodeAddress, uint256 _ethSlashAmount) external;
                    }
                    /**
                      *       .
                      *      / \\
                      *     |.'.|
                      *     |'.'|
                      *   ,'|   |`.
                      *  |,-'-|-'-.|
                      *   __|_| |         _        _      _____           _
                      *  | ___ \\|        | |      | |    | ___ \\         | |
                      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                      * +---------------------------------------------------+
                      * |  DECENTRALISED STAKING PROTOCOL FOR ETHEREUM 2.0  |
                      * +---------------------------------------------------+
                      *
                      *  Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned,
                      *  decentralised, trustless and compatible with staking in Ethereum 2.0.
                      *
                      *  For more information about Rocket Pool, visit https://rocketpool.net
                      *
                      *  Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty
                      *
                      */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity 0.7.6;
                    import "@openzeppelin/contracts/math/SafeMath.sol";
                    import "./RocketNodeDistributorStorageLayout.sol";
                    import "../../interface/RocketStorageInterface.sol";
                    import "../../interface/node/RocketNodeManagerInterface.sol";
                    import "../../interface/node/RocketNodeDistributorInterface.sol";
                    import "../../interface/node/RocketNodeStakingInterface.sol";
                    /// @dev Contains the logic for RocketNodeDistributors
                    contract RocketNodeDistributorDelegate is RocketNodeDistributorStorageLayout, RocketNodeDistributorInterface {
                        // Import libraries
                        using SafeMath for uint256;
                        // Events
                        event FeesDistributed(address _nodeAddress, uint256 _userAmount, uint256 _nodeAmount, uint256 _time);
                        // Constants
                        uint8 public constant version = 2;
                        uint256 constant calcBase = 1 ether;
                        uint256 private constant NOT_ENTERED = 1;
                        uint256 private constant ENTERED = 2;
                        // Precomputed constants
                        bytes32 immutable rocketNodeManagerKey;
                        bytes32 immutable rocketNodeStakingKey;
                        bytes32 immutable rocketTokenRETHKey;
                        modifier nonReentrant() {
                            require(lock != ENTERED, "Reentrant call");
                            lock = ENTERED;
                            _;
                            lock = NOT_ENTERED;
                        }
                        constructor() {
                            // Precompute storage keys
                            rocketNodeManagerKey = keccak256(abi.encodePacked("contract.address", "rocketNodeManager"));
                            rocketNodeStakingKey = keccak256(abi.encodePacked("contract.address", "rocketNodeStaking"));
                            rocketTokenRETHKey = keccak256(abi.encodePacked("contract.address", "rocketTokenRETH"));
                            // These values must be set by proxy contract as this contract should only be delegatecalled
                            rocketStorage = RocketStorageInterface(address(0));
                            nodeAddress = address(0);
                            lock = NOT_ENTERED;
                        }
                        /// @notice Returns the portion of the contract's balance that belongs to the node operator
                        function getNodeShare() override public view returns (uint256) {
                            // Get contracts
                            RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(rocketStorage.getAddress(rocketNodeManagerKey));
                            RocketNodeStakingInterface rocketNodeStaking = RocketNodeStakingInterface(rocketStorage.getAddress(rocketNodeStakingKey));
                            // Get withdrawal address and the node's average node fee
                            uint256 averageNodeFee = rocketNodeManager.getAverageNodeFee(nodeAddress);
                            // Get node ETH collateral ratio
                            uint256 collateralRatio = rocketNodeStaking.getNodeETHCollateralisationRatio(nodeAddress);
                            // Calculate reward split
                            uint256 nodeBalance = address(this).balance.mul(calcBase).div(collateralRatio);
                            uint256 userBalance = address(this).balance.sub(nodeBalance);
                            return nodeBalance.add(userBalance.mul(averageNodeFee).div(calcBase));
                        }
                        /// @notice Returns the portion of the contract's balance that belongs to the users
                        function getUserShare() override external view returns (uint256) {
                            return address(this).balance.sub(getNodeShare());
                        }
                        /// @notice Distributes the balance of this contract to its owners
                        function distribute() override external nonReentrant {
                            // Calculate node share
                            uint256 nodeShare = getNodeShare();
                            // Transfer node share
                            address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(nodeAddress);
                            (bool success,) = withdrawalAddress.call{value : nodeShare}("");
                            require(success);
                            // Transfer user share
                            uint256 userShare = address(this).balance;
                            address rocketTokenRETH = rocketStorage.getAddress(rocketTokenRETHKey);
                            payable(rocketTokenRETH).transfer(userShare);
                            // Emit event
                            emit FeesDistributed(nodeAddress, userShare, nodeShare, block.timestamp);
                        }
                    }
                    

                    File 5 of 10: RocketNodeManager
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    /// @title Base settings / modifiers for each contract in Rocket Pool
                    /// @author David Rugendyke
                    abstract contract RocketBase {
                        // Calculate using this as the base
                        uint256 constant calcBase = 1 ether;
                        // Version of the contract
                        uint8 public version;
                        // The main storage contract where primary persistant storage is maintained
                        RocketStorageInterface rocketStorage = RocketStorageInterface(address(0));
                        /*** Modifiers **********************************************************/
                        /**
                        * @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
                        */
                        modifier onlyLatestNetworkContract() {
                            require(getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))), "Invalid or outdated network contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that doesn't match one of the supplied contract or is the latest version of that contract
                        */
                        modifier onlyLatestContract(string memory _contractName, address _contractAddress) {
                            require(_contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", _contractName))), "Invalid or outdated contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered node
                        */
                        modifier onlyRegisteredNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a trusted node DAO member
                        */
                        modifier onlyTrustedNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("dao.trustednodes.", "member", _nodeAddress))), "Invalid trusted node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered minipool
                        */
                        modifier onlyRegisteredMinipool(address _minipoolAddress) {
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress))), "Invalid minipool");
                            _;
                        }
                        
                        /**
                        * @dev Throws if called by any account other than a guardian account (temporary account allowed access to settings before DAO is fully enabled)
                        */
                        modifier onlyGuardian() {
                            require(msg.sender == rocketStorage.getGuardian(), "Account is not a temporary guardian");
                            _;
                        }
                        /*** Methods **********************************************************/
                        /// @dev Set the main Rocket Storage address
                        constructor(RocketStorageInterface _rocketStorageAddress) {
                            // Update the contract address
                            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
                        }
                        /// @dev Get the address of a network contract by name
                        function getContractAddress(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Check it
                            require(contractAddress != address(0x0), "Contract not found");
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)
                        function getContractAddressUnsafe(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the name of a network contract by address
                        function getContractName(address _contractAddress) internal view returns (string memory) {
                            // Get the contract name
                            string memory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
                            // Check it
                            require(bytes(contractName).length > 0, "Contract not found");
                            // Return
                            return contractName;
                        }
                        /// @dev Get revert error message from a .call method
                        function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return "Transaction reverted silently";
                            assembly {
                                // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string)); // All that remains is the revert string
                        }
                        /*** Rocket Storage Methods ****************************************/
                        // Note: Unused helpers have been removed to keep contract sizes down
                        /// @dev Storage get methods
                        function getAddress(bytes32 _key) internal view returns (address) { return rocketStorage.getAddress(_key); }
                        function getUint(bytes32 _key) internal view returns (uint) { return rocketStorage.getUint(_key); }
                        function getString(bytes32 _key) internal view returns (string memory) { return rocketStorage.getString(_key); }
                        function getBytes(bytes32 _key) internal view returns (bytes memory) { return rocketStorage.getBytes(_key); }
                        function getBool(bytes32 _key) internal view returns (bool) { return rocketStorage.getBool(_key); }
                        function getInt(bytes32 _key) internal view returns (int) { return rocketStorage.getInt(_key); }
                        function getBytes32(bytes32 _key) internal view returns (bytes32) { return rocketStorage.getBytes32(_key); }
                        /// @dev Storage set methods
                        function setAddress(bytes32 _key, address _value) internal { rocketStorage.setAddress(_key, _value); }
                        function setUint(bytes32 _key, uint _value) internal { rocketStorage.setUint(_key, _value); }
                        function setString(bytes32 _key, string memory _value) internal { rocketStorage.setString(_key, _value); }
                        function setBytes(bytes32 _key, bytes memory _value) internal { rocketStorage.setBytes(_key, _value); }
                        function setBool(bytes32 _key, bool _value) internal { rocketStorage.setBool(_key, _value); }
                        function setInt(bytes32 _key, int _value) internal { rocketStorage.setInt(_key, _value); }
                        function setBytes32(bytes32 _key, bytes32 _value) internal { rocketStorage.setBytes32(_key, _value); }
                        /// @dev Storage delete methods
                        function deleteAddress(bytes32 _key) internal { rocketStorage.deleteAddress(_key); }
                        function deleteUint(bytes32 _key) internal { rocketStorage.deleteUint(_key); }
                        function deleteString(bytes32 _key) internal { rocketStorage.deleteString(_key); }
                        function deleteBytes(bytes32 _key) internal { rocketStorage.deleteBytes(_key); }
                        function deleteBool(bytes32 _key) internal { rocketStorage.deleteBool(_key); }
                        function deleteInt(bytes32 _key) internal { rocketStorage.deleteInt(_key); }
                        function deleteBytes32(bytes32 _key) internal { rocketStorage.deleteBytes32(_key); }
                        /// @dev Storage arithmetic methods
                        function addUint(bytes32 _key, uint256 _amount) internal { rocketStorage.addUint(_key, _amount); }
                        function subUint(bytes32 _key, uint256 _amount) internal { rocketStorage.subUint(_key, _amount); }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents a minipool's status within the network
                    enum MinipoolStatus {
                        Initialised,    // The minipool has been initialised and is awaiting a deposit of user ETH
                        Prelaunch,      // The minipool has enough ETH to begin staking and is awaiting launch by the node operator
                        Staking,        // The minipool is currently staking
                        Withdrawable,   // NO LONGER USED
                        Dissolved       // The minipool has been dissolved and its user deposited ETH has been returned to the deposit pool
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // A struct containing all the information on-chain about a specific node
                    struct NodeDetails {
                        bool exists;
                        uint256 registrationTime;
                        string timezoneLocation;
                        bool feeDistributorInitialised;
                        address feeDistributorAddress;
                        uint256 rewardNetwork;
                        uint256 rplStake;
                        uint256 effectiveRPLStake;
                        uint256 minimumRPLStake;
                        uint256 maximumRPLStake;
                        uint256 ethMatched;
                        uint256 ethMatchedLimit;
                        uint256 minipoolCount;
                        uint256 balanceETH;
                        uint256 balanceRETH;
                        uint256 balanceRPL;
                        uint256 balanceOldRPL;
                        uint256 depositCreditBalance;
                        uint256 distributorBalanceUserETH;
                        uint256 distributorBalanceNodeETH;
                        address withdrawalAddress;
                        address pendingWithdrawalAddress;
                        bool smoothingPoolRegistrationState;
                        uint256 smoothingPoolRegistrationChanged;
                        address nodeAddress;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    import "../../types/NodeDetails.sol";
                    interface RocketNodeManagerInterface {
                        // Structs
                        struct TimezoneCount {
                            string timezone;
                            uint256 count;
                        }
                        function getNodeCount() external view returns (uint256);
                        function getNodeCountPerTimezone(uint256 offset, uint256 limit) external view returns (TimezoneCount[] memory);
                        function getNodeAt(uint256 _index) external view returns (address);
                        function getNodeExists(address _nodeAddress) external view returns (bool);
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeRPLWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeRPLWithdrawalAddressIsSet(address _nodeAddress) external view returns (bool);
                        function unsetRPLWithdrawalAddress(address _nodeAddress) external;
                        function setRPLWithdrawalAddress(address _nodeAddress, address _newRPLWithdrawalAddress, bool _confirm) external;
                        function confirmRPLWithdrawalAddress(address _nodeAddress) external;
                        function getNodePendingRPLWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeTimezoneLocation(address _nodeAddress) external view returns (string memory);
                        function registerNode(string calldata _timezoneLocation) external;
                        function getNodeRegistrationTime(address _nodeAddress) external view returns (uint256);
                        function setTimezoneLocation(string calldata _timezoneLocation) external;
                        function setRewardNetwork(address _nodeAddress, uint256 network) external;
                        function getRewardNetwork(address _nodeAddress) external view returns (uint256);
                        function getFeeDistributorInitialised(address _nodeAddress) external view returns (bool);
                        function initialiseFeeDistributor() external;
                        function getAverageNodeFee(address _nodeAddress) external view returns (uint256);
                        function setSmoothingPoolRegistrationState(bool _state) external;
                        function getSmoothingPoolRegistrationState(address _nodeAddress) external returns (bool);
                        function getSmoothingPoolRegistrationChanged(address _nodeAddress) external returns (uint256);
                        function getSmoothingPoolRegisteredNodeCount(uint256 _offset, uint256 _limit) external view returns (uint256);
                        function getNodeDetails(address _nodeAddress) external view returns (NodeDetails memory);
                        function getNodeAddresses(uint256 _offset, uint256 _limit) external view returns (address[] memory);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAOProtocolSettingsNodeInterface {
                        function getRegistrationEnabled() external view returns (bool);
                        function getSmoothingPoolRegistrationEnabled() external view returns (bool);
                        function getDepositEnabled() external view returns (bool);
                        function getVacantMinipoolsEnabled() external view returns (bool);
                        function getMinimumPerMinipoolStake() external view returns (uint256);
                        function getMaximumPerMinipoolStake() external view returns (uint256);
                        function getMaximumStakeForVotingPower() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface AddressSetStorageInterface {
                        function getCount(bytes32 _key) external view returns (uint);
                        function getItem(bytes32 _key, uint _index) external view returns (address);
                        function getIndexOf(bytes32 _key, address _value) external view returns (int);
                        function addItem(bytes32 _key, address _value) external;
                        function removeItem(bytes32 _key, address _value) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNodeDistributorFactoryInterface {
                        function getProxyBytecode() external pure returns (bytes memory);
                        function getProxyAddress(address _nodeAddress) external view returns(address);
                        function createProxy(address _nodeAddress) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents the type of deposits required by a minipool
                    enum MinipoolDeposit {
                        None,       // Marks an invalid deposit type
                        Full,       // The minipool requires 32 ETH from the node operator, 16 ETH of which will be refinanced from user deposits
                        Half,       // The minipool required 16 ETH from the node operator to be matched with 16 ETH from user deposits
                        Empty,      // The minipool requires 0 ETH from the node operator to be matched with 32 ETH from user deposits (trusted nodes only)
                        Variable    // Indicates this minipool is of the new generation that supports a variable deposit amount
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "./MinipoolDeposit.sol";
                    import "./MinipoolStatus.sol";
                    // A struct containing all the information on-chain about a specific minipool
                    struct MinipoolDetails {
                        bool exists;
                        address minipoolAddress;
                        bytes pubkey;
                        MinipoolStatus status;
                        uint256 statusBlock;
                        uint256 statusTime;
                        bool finalised;
                        MinipoolDeposit depositType;
                        uint256 nodeFee;
                        uint256 nodeDepositBalance;
                        bool nodeDepositAssigned;
                        uint256 userDepositBalance;
                        bool userDepositAssigned;
                        uint256 userDepositAssignedTime;
                        bool useLatestDelegate;
                        address delegate;
                        address previousDelegate;
                        address effectiveDelegate;
                        uint256 penaltyCount;
                        uint256 penaltyRate;
                        address nodeAddress;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolStatus.sol";
                    import "../RocketStorageInterface.sol";
                    interface RocketMinipoolInterface {
                        function version() external view returns (uint8);
                        function initialise(address _nodeAddress) external;
                        function getStatus() external view returns (MinipoolStatus);
                        function getFinalised() external view returns (bool);
                        function getStatusBlock() external view returns (uint256);
                        function getStatusTime() external view returns (uint256);
                        function getScrubVoted(address _member) external view returns (bool);
                        function getDepositType() external view returns (MinipoolDeposit);
                        function getNodeAddress() external view returns (address);
                        function getNodeFee() external view returns (uint256);
                        function getNodeDepositBalance() external view returns (uint256);
                        function getNodeRefundBalance() external view returns (uint256);
                        function getNodeDepositAssigned() external view returns (bool);
                        function getPreLaunchValue() external view returns (uint256);
                        function getNodeTopUpValue() external view returns (uint256);
                        function getVacant() external view returns (bool);
                        function getPreMigrationBalance() external view returns (uint256);
                        function getUserDistributed() external view returns (bool);
                        function getUserDepositBalance() external view returns (uint256);
                        function getUserDepositAssigned() external view returns (bool);
                        function getUserDepositAssignedTime() external view returns (uint256);
                        function getTotalScrubVotes() external view returns (uint256);
                        function calculateNodeShare(uint256 _balance) external view returns (uint256);
                        function calculateUserShare(uint256 _balance) external view returns (uint256);
                        function preDeposit(uint256 _bondingValue, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot) external payable;
                        function deposit() external payable;
                        function userDeposit() external payable;
                        function distributeBalance(bool _rewardsOnly) external;
                        function beginUserDistribute() external;
                        function userDistributeAllowed() external view returns (bool);
                        function refund() external;
                        function slash() external;
                        function finalise() external;
                        function canStake() external view returns (bool);
                        function canPromote() external view returns (bool);
                        function stake(bytes calldata _validatorSignature, bytes32 _depositDataRoot) external;
                        function prepareVacancy(uint256 _bondAmount, uint256 _currentBalance) external;
                        function promote() external;
                        function dissolve() external;
                        function close() external;
                        function voteScrub() external;
                        function reduceBondAmount() external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolDetails.sol";
                    import "./RocketMinipoolInterface.sol";
                    interface RocketMinipoolManagerInterface {
                        function getMinipoolCount() external view returns (uint256);
                        function getStakingMinipoolCount() external view returns (uint256);
                        function getFinalisedMinipoolCount() external view returns (uint256);
                        function getActiveMinipoolCount() external view returns (uint256);
                        function getMinipoolRPLSlashed(address _minipoolAddress) external view returns (bool);
                        function getMinipoolCountPerStatus(uint256 offset, uint256 limit) external view returns (uint256, uint256, uint256, uint256, uint256);
                        function getPrelaunchMinipools(uint256 offset, uint256 limit) external view returns (address[] memory);
                        function getMinipoolAt(uint256 _index) external view returns (address);
                        function getNodeMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeActiveMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeFinalisedMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeStakingMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeStakingMinipoolCountBySize(address _nodeAddress, uint256 _depositSize) external view returns (uint256);
                        function getNodeMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address);
                        function getNodeValidatingMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeValidatingMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address);
                        function getMinipoolByPubkey(bytes calldata _pubkey) external view returns (address);
                        function getMinipoolExists(address _minipoolAddress) external view returns (bool);
                        function getMinipoolDestroyed(address _minipoolAddress) external view returns (bool);
                        function getMinipoolPubkey(address _minipoolAddress) external view returns (bytes memory);
                        function updateNodeStakingMinipoolCount(uint256 _previousBond, uint256 _newBond, uint256 _previousFee, uint256 _newFee) external;
                        function getMinipoolWithdrawalCredentials(address _minipoolAddress) external pure returns (bytes memory);
                        function createMinipool(address _nodeAddress, uint256 _salt) external returns (RocketMinipoolInterface);
                        function createVacantMinipool(address _nodeAddress, uint256 _salt, bytes calldata _validatorPubkey, uint256 _bondAmount, uint256 _currentBalance) external returns (RocketMinipoolInterface);
                        function removeVacantMinipool() external;
                        function getVacantMinipoolCount() external view returns (uint256);
                        function getVacantMinipoolAt(uint256 _index) external view returns (address);
                        function destroyMinipool() external;
                        function incrementNodeStakingMinipoolCount(address _nodeAddress) external;
                        function decrementNodeStakingMinipoolCount(address _nodeAddress) external;
                        function tryDistribute(address _nodeAddress) external;
                        function incrementNodeFinalisedMinipoolCount(address _nodeAddress) external;
                        function setMinipoolPubkey(bytes calldata _pubkey) external;
                        function getMinipoolDepositType(address _minipoolAddress) external view returns (MinipoolDeposit);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNodeDistributorInterface {
                        function getNodeShare() external view returns (uint256);
                        function getUserShare() external view returns (uint256);
                        function distribute() external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    interface RocketDAONodeTrustedSettingsRewardsInterface {
                        function getNetworkEnabled(uint256 _network) external view returns (bool);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    interface RocketDAOProtocolSettingsRewardsInterface {
                        function setSettingRewardsClaimers(uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent) external;
                        function getRewardsClaimerPerc(string memory _contractName) external view returns (uint256);
                        function getRewardsClaimersPerc() external view returns (uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent);
                        function getRewardsClaimersTrustedNodePerc() external view returns (uint256);
                        function getRewardsClaimersProtocolPerc() external view returns (uint256);
                        function getRewardsClaimersNodePerc() external view returns (uint256);
                        function getRewardsClaimersTimeUpdated() external view returns (uint256);
                        function getRewardsClaimIntervalPeriods() external view returns (uint256);
                        function getRewardsClaimIntervalTime() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    interface RocketNodeStakingInterface {
                        function getTotalRPLStake() external view returns (uint256);
                        function getNodeRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatched(address _nodeAddress) external view returns (uint256);
                        function getNodeETHProvided(address _nodeAddress) external view returns (uint256);
                        function getNodeETHCollateralisationRatio(address _nodeAddress) external view returns (uint256);
                        function getNodeRPLStakedTime(address _nodeAddress) external view returns (uint256);
                        function getNodeEffectiveRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMinimumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMaximumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatchedLimit(address _nodeAddress) external view returns (uint256);
                        function getRPLLockingAllowed(address _nodeAddress) external view returns (bool);
                        function stakeRPL(uint256 _amount) external;
                        function stakeRPLFor(address _nodeAddress, uint256 _amount) external;
                        function setRPLLockingAllowed(address _nodeAddress, bool _allowed) external;
                        function setStakeRPLForAllowed(address _caller, bool _allowed) external;
                        function setStakeRPLForAllowed(address _nodeAddress, address _caller, bool _allowed) external;
                        function getNodeRPLLocked(address _nodeAddress) external view returns (uint256);
                        function lockRPL(address _nodeAddress, uint256 _amount) external;
                        function unlockRPL(address _nodeAddress, uint256 _amount) external;
                        function transferRPL(address _from, address _to, uint256 _amount) external;
                        function withdrawRPL(uint256 _amount) external;
                        function withdrawRPL(address _nodeAddress, uint256 _amount) external;
                        function slashRPL(address _nodeAddress, uint256 _ethSlashAmount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    interface RocketNodeDepositInterface {
                        function getNodeDepositCredit(address _nodeAddress) external view returns (uint256);
                        function getNodeEthBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeCreditAndBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeUsableCreditAndBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeUsableCredit(address _nodeAddress) external view returns (uint256);
                        function increaseDepositCreditBalance(address _nodeOperator, uint256 _amount) external;
                        function depositEthFor(address _nodeAddress) external payable;
                        function withdrawEth(address _nodeAddress, uint256 _amount) external;
                        function deposit(uint256 _depositAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) external payable;
                        function depositWithCredit(uint256 _depositAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) external payable;
                        function isValidDepositAmount(uint256 _amount) external pure returns (bool);
                        function getDepositAmounts() external pure returns (uint256[] memory);
                        function createVacantMinipool(uint256 _bondAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, uint256 _salt, address _expectedMinipoolAddress, uint256 _currentBalance) external;
                        function increaseEthMatched(address _nodeAddress, uint256 _amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../../../types/MinipoolDeposit.sol";
                    interface RocketDAOProtocolSettingsMinipoolInterface {
                        function getLaunchBalance() external view returns (uint256);
                        function getPreLaunchValue() external pure returns (uint256);
                        function getDepositUserAmount(MinipoolDeposit _depositType) external view returns (uint256);
                        function getFullDepositUserAmount() external view returns (uint256);
                        function getHalfDepositUserAmount() external view returns (uint256);
                        function getVariableDepositAmount() external view returns (uint256);
                        function getSubmitWithdrawableEnabled() external view returns (bool);
                        function getBondReductionEnabled() external view returns (bool);
                        function getLaunchTimeout() external view returns (uint256);
                        function getMaximumCount() external view returns (uint256);
                        function isWithinUserDistributeWindow(uint256 _time) external view returns (bool);
                        function hasUserDistributeWindowPassed(uint256 _time) external view returns (bool);
                        function getUserDistributeWindowStart() external view returns (uint256);
                        function getUserDistributeWindowLength() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                    pragma solidity >0.5.0 <0.9.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);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    struct Checkpoint224 {
                        uint32 _block;
                        uint224 _value;
                    }
                    /// @notice Accounting for snapshotting of values based on block numbers
                    interface RocketNetworkSnapshotsInterface {
                        function push(bytes32 _key, uint224 _value) external;
                        function length(bytes32 _key) external view returns (uint256);
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224);
                        function latestBlock(bytes32 _key) external view returns (uint32);
                        function latestValue(bytes32 _key) external view returns (uint224);
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224);
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity 0.8.18;
                    pragma abicoder v2;
                    import "../RocketBase.sol";
                    import "../../types/MinipoolStatus.sol";
                    import "../../types/NodeDetails.sol";
                    import "../../interface/node/RocketNodeManagerInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
                    import "../../interface/util/AddressSetStorageInterface.sol";
                    import "../../interface/node/RocketNodeDistributorFactoryInterface.sol";
                    import "../../interface/minipool/RocketMinipoolManagerInterface.sol";
                    import "../../interface/node/RocketNodeDistributorInterface.sol";
                    import "../../interface/dao/node/settings/RocketDAONodeTrustedSettingsRewardsInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsRewardsInterface.sol";
                    import "../../interface/node/RocketNodeStakingInterface.sol";
                    import "../../interface/node/RocketNodeDepositInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
                    import "../../interface/util/IERC20.sol";
                    import "../../interface/network/RocketNetworkSnapshotsInterface.sol";
                    /// @notice Node registration and management
                    contract RocketNodeManager is RocketBase, RocketNodeManagerInterface {
                        // Events
                        event NodeRegistered(address indexed node, uint256 time);
                        event NodeTimezoneLocationSet(address indexed node, uint256 time);
                        event NodeRewardNetworkChanged(address indexed node, uint256 network);
                        event NodeSmoothingPoolStateChanged(address indexed node, bool state);
                        event NodeRPLWithdrawalAddressSet(address indexed node, address indexed withdrawalAddress, uint256 time);
                        event NodeRPLWithdrawalAddressUnset(address indexed node, uint256 time);
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            version = 4;
                        }
                        /// @notice Get the number of nodes in the network
                        function getNodeCount() override public view returns (uint256) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getCount(keccak256(abi.encodePacked("nodes.index")));
                        }
                        /// @notice Get a breakdown of the number of nodes per timezone
                        function getNodeCountPerTimezone(uint256 _offset, uint256 _limit) override external view returns (TimezoneCount[] memory) {
                            // Get contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Precompute node key
                            bytes32 nodeKey = keccak256(abi.encodePacked("nodes.index"));
                            // Calculate range
                            uint256 totalNodes = addressSetStorage.getCount(nodeKey);
                            uint256 max = _offset + _limit;
                            if (max > totalNodes || _limit == 0) { max = totalNodes; }
                            // Create an array with as many elements as there are potential values to return
                            TimezoneCount[] memory counts = new TimezoneCount[](max - _offset);
                            uint256 uniqueTimezoneCount = 0;
                            // Iterate the minipool range
                            for (uint256 i = _offset; i < max; ++i) {
                                address nodeAddress = addressSetStorage.getItem(nodeKey, i);
                                string memory timezone = getString(keccak256(abi.encodePacked("node.timezone.location", nodeAddress)));
                                // Find existing entry in our array
                                bool existing = false;
                                for (uint256 j = 0; j < uniqueTimezoneCount; ++j) {
                                    if (keccak256(bytes(counts[j].timezone)) == keccak256(bytes(timezone))) {
                                        existing = true;
                                        // Increment the counter
                                        counts[j].count++;
                                        break;
                                    }
                                }
                                // Entry was not found, so create a new one
                                if (!existing) {
                                    counts[uniqueTimezoneCount].timezone = timezone;
                                    counts[uniqueTimezoneCount].count = 1;
                                    uniqueTimezoneCount++;
                                }
                            }
                            // Dirty hack to cut unused elements off end of return value
                            assembly {
                                mstore(counts, uniqueTimezoneCount)
                            }
                            return counts;
                        }
                        /// @notice Get a node address by index
                        function getNodeAt(uint256 _index) override external view returns (address) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getItem(keccak256(abi.encodePacked("nodes.index")), _index);
                        }
                        /// @notice Check whether a node exists
                        function getNodeExists(address _nodeAddress) override public view returns (bool) {
                            return getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress)));
                        }
                        /// @notice Get a node's current withdrawal address
                        function getNodeWithdrawalAddress(address _nodeAddress) override public view returns (address) {
                            return rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                        }
                        /// @notice Get a node's pending withdrawal address
                        function getNodePendingWithdrawalAddress(address _nodeAddress) override public view returns (address) {
                            return rocketStorage.getNodePendingWithdrawalAddress(_nodeAddress);
                        }
                        /// @notice Get a node's current RPL withdrawal address
                        function getNodeRPLWithdrawalAddress(address _nodeAddress) override public view returns (address) {
                            address withdrawalAddress = getAddress(keccak256(abi.encodePacked("node.rpl.withdrawal.address", _nodeAddress)));
                            if (withdrawalAddress == address(0)) {
                                // Defaults to current withdrawal address if unset
                                return rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                            }
                            return withdrawalAddress;
                        }
                        /// @notice Get a node's pending RPL withdrawal address
                        function getNodePendingRPLWithdrawalAddress(address _nodeAddress) override public view returns (address) {
                            return getAddress(keccak256(abi.encodePacked("node.pending.rpl.withdrawal.address", _nodeAddress)));
                        }
                        /// @notice Returns true if a node has set an RPL withdrawal address
                        function getNodeRPLWithdrawalAddressIsSet(address _nodeAddress) override external view returns (bool) {
                            return(getAddress(keccak256(abi.encodePacked("node.rpl.withdrawal.address", _nodeAddress))) != address(0));
                        }
                        /// @notice Unsets a node operator's RPL withdrawal address returning it to the default
                        function unsetRPLWithdrawalAddress(address _nodeAddress) external override onlyRegisteredNode(_nodeAddress) {
                            bytes32 addressKey = keccak256(abi.encodePacked("node.rpl.withdrawal.address", _nodeAddress));
                            // Confirm the transaction is from the node's current RPL withdrawal address
                            require(getAddress(addressKey) == msg.sender, "Only a tx from a node's RPL withdrawal address can unset it");
                            // Unset the address
                            deleteAddress(addressKey);
                            // Emit withdrawal address unset event
                            emit NodeRPLWithdrawalAddressUnset(_nodeAddress, block.timestamp);
                        }
                        // @notice Set a node's RPL withdrawal address
                        function setRPLWithdrawalAddress(address _nodeAddress, address _newRPLWithdrawalAddress, bool _confirm) external override onlyRegisteredNode(_nodeAddress) {
                            // Check new RPL withdrawal address
                            require(_newRPLWithdrawalAddress != address(0x0), "Invalid RPL withdrawal address");
                            // Confirm the transaction is from the node's current RPL withdrawal address
                            address withdrawalAddress = getNodeRPLWithdrawalAddress(_nodeAddress);
                            require(withdrawalAddress == msg.sender, "Only a tx from a node's RPL withdrawal address can update it");
                            // Update immediately if confirmed
                            if (_confirm) {
                                // Delete any existing pending update
                                deleteAddress(keccak256(abi.encodePacked("node.pending.rpl.withdrawal.address", _nodeAddress)));
                                // Perform the update
                                updateRPLWithdrawalAddress(_nodeAddress, _newRPLWithdrawalAddress);
                            }
                            // Set pending withdrawal address if not confirmed
                            else {
                                setAddress(keccak256(abi.encodePacked("node.pending.rpl.withdrawal.address", _nodeAddress)), _newRPLWithdrawalAddress);
                            }
                        }
                        /// @notice Confirm a node's new RPL withdrawal address
                        function confirmRPLWithdrawalAddress(address _nodeAddress) external override onlyRegisteredNode(_nodeAddress) {
                            bytes32 pendingKey = keccak256(abi.encodePacked("node.pending.rpl.withdrawal.address", _nodeAddress));
                            // Get node by pending withdrawal address
                            require(getAddress(pendingKey) == msg.sender, "Confirmation must come from the pending RPL withdrawal address");
                            deleteAddress(pendingKey);
                            // Update withdrawal address
                            updateRPLWithdrawalAddress(_nodeAddress, msg.sender);
                        }
                        /// @notice Update a node's withdrawal address
                        function updateRPLWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress) private {
                            // Set new withdrawal address
                            setAddress(keccak256(abi.encodePacked("node.rpl.withdrawal.address", _nodeAddress)), _newWithdrawalAddress);
                            // Emit withdrawal address set event
                            emit NodeRPLWithdrawalAddressSet(_nodeAddress, _newWithdrawalAddress, block.timestamp);
                        }
                        /// @notice Get a node's timezone location
                        function getNodeTimezoneLocation(address _nodeAddress) override public view returns (string memory) {
                            return getString(keccak256(abi.encodePacked("node.timezone.location", _nodeAddress)));
                        }
                        /// @notice Register a new node with Rocket Pool
                        function registerNode(string calldata _timezoneLocation) override external onlyLatestContract("rocketNodeManager", address(this)) {
                            // Load contracts
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            // Check node settings
                            require(rocketDAOProtocolSettingsNode.getRegistrationEnabled(), "Rocket Pool node registrations are currently disabled");
                            // Check timezone location
                            require(bytes(_timezoneLocation).length >= 4, "The timezone location is invalid");
                            // Initialise node data
                            setBool(keccak256(abi.encodePacked("node.exists", msg.sender)), true);
                            setBool(keccak256(abi.encodePacked("node.voting.enabled", msg.sender)), true);
                            setString(keccak256(abi.encodePacked("node.timezone.location", msg.sender)), _timezoneLocation);
                            // Add node to index
                            bytes32 nodeIndexKey = keccak256(abi.encodePacked("nodes.index"));
                            addressSetStorage.addItem(nodeIndexKey, msg.sender);
                            // Initialise fee distributor for this node
                            _initialiseFeeDistributor(msg.sender);
                            // Set node registration time (uses old storage key name for backwards compatibility)
                            setUint(keccak256(abi.encodePacked("rewards.pool.claim.contract.registered.time", "rocketClaimNode", msg.sender)), block.timestamp);
                            // Update count
                            rocketNetworkSnapshots.push(keccak256(abi.encodePacked("node.count")), uint224(addressSetStorage.getCount(nodeIndexKey)));
                            // Default voting delegate to themself
                            rocketNetworkSnapshots.push(keccak256(abi.encodePacked("node.delegate", msg.sender)), uint224(uint160(msg.sender)));
                            // Emit node registered event
                            emit NodeRegistered(msg.sender, block.timestamp);
                        }
                        /// @notice Gets the timestamp of when a node was registered
                        function getNodeRegistrationTime(address _nodeAddress) onlyRegisteredNode(_nodeAddress) override public view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("rewards.pool.claim.contract.registered.time", "rocketClaimNode", _nodeAddress)));
                        }
                        /// @notice Set a node's timezone location
                        function setTimezoneLocation(string calldata _timezoneLocation) override external onlyLatestContract("rocketNodeManager", address(this)) onlyRegisteredNode(msg.sender) {
                            // Check timezone location
                            require(bytes(_timezoneLocation).length >= 4, "The timezone location is invalid");
                            // Set timezone location
                            setString(keccak256(abi.encodePacked("node.timezone.location", msg.sender)), _timezoneLocation);
                            // Emit node timezone location set event
                            emit NodeTimezoneLocationSet(msg.sender, block.timestamp);
                        }
                        /// @notice Returns true if node has initialised their fee distributor contract
                        function getFeeDistributorInitialised(address _nodeAddress) override public view returns (bool) {
                            // Load contracts
                            RocketNodeDistributorFactoryInterface rocketNodeDistributorFactory = RocketNodeDistributorFactoryInterface(getContractAddress("rocketNodeDistributorFactory"));
                            // Get distributor address
                            address contractAddress = rocketNodeDistributorFactory.getProxyAddress(_nodeAddress);
                            // Check if contract exists at that address
                            uint32 codeSize;
                            assembly {
                                codeSize := extcodesize(contractAddress)
                            }
                            return codeSize > 0;
                        }
                        /// @notice Node operators created before the distributor was implemented must call this to setup their distributor contract
                        function initialiseFeeDistributor() override external onlyLatestContract("rocketNodeManager", address(this)) onlyRegisteredNode(msg.sender) {
                            // Prevent multiple calls
                            require(!getFeeDistributorInitialised(msg.sender), "Already initialised");
                            // Load contracts
                            RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                            // Calculate and set current average fee numerator
                            uint256 count = rocketMinipoolManager.getNodeMinipoolCount(msg.sender);
                            if (count > 0){
                                uint256 numerator = 0;
                                // Note: this loop is safe as long as all current node operators at the time of upgrade have few enough minipools
                                for (uint256 i = 0; i < count; ++i) {
                                    RocketMinipoolInterface minipool = RocketMinipoolInterface(rocketMinipoolManager.getNodeMinipoolAt(msg.sender, i));
                                    if (minipool.getStatus() == MinipoolStatus.Staking){
                                        numerator = numerator + minipool.getNodeFee();
                                    }
                                }
                                setUint(keccak256(abi.encodePacked("node.average.fee.numerator", msg.sender)), numerator);
                            }
                            // Create the distributor contract
                            _initialiseFeeDistributor(msg.sender);
                        }
                        /// @notice Deploys the fee distributor contract for a given node
                        function _initialiseFeeDistributor(address _nodeAddress) internal {
                            // Load contracts
                            RocketNodeDistributorFactoryInterface rocketNodeDistributorFactory = RocketNodeDistributorFactoryInterface(getContractAddress("rocketNodeDistributorFactory"));
                            // Create the distributor proxy
                            rocketNodeDistributorFactory.createProxy(_nodeAddress);
                        }
                        /// @notice Calculates a nodes average node fee
                        function getAverageNodeFee(address _nodeAddress) override external view returns (uint256) {
                            // Load contracts
                            RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                            RocketNodeDepositInterface rocketNodeDeposit = RocketNodeDepositInterface(getContractAddress("rocketNodeDeposit"));
                            RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
                            // Get valid deposit amounts
                            uint256[] memory depositSizes = rocketNodeDeposit.getDepositAmounts();
                            // Setup memory for calculations
                            uint256[] memory depositWeights = new uint256[](depositSizes.length);
                            uint256[] memory depositCounts = new uint256[](depositSizes.length);
                            uint256 depositWeightTotal;
                            uint256 totalCount;
                            uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
                            // Retrieve the number of staking minipools per deposit size
                            for (uint256 i = 0; i < depositSizes.length; ++i) {
                                depositCounts[i] = rocketMinipoolManager.getNodeStakingMinipoolCountBySize(_nodeAddress, depositSizes[i]);
                                totalCount = totalCount + depositCounts[i];
                            }
                            if (totalCount == 0) {
                                return 0;
                            }
                            // Calculate the weights of each deposit size
                            for (uint256 i = 0; i < depositSizes.length; ++i) {
                                depositWeights[i] = (launchAmount - depositSizes[i]) * depositCounts[i];
                                depositWeightTotal = depositWeightTotal + depositWeights[i];
                            }
                            for (uint256 i = 0; i < depositSizes.length; ++i) {
                                depositWeights[i] = depositWeights[i] * calcBase / depositWeightTotal;
                            }
                            // Calculate the weighted average
                            uint256 weightedAverage = 0;
                            for (uint256 i = 0; i < depositSizes.length; ++i) {
                                if (depositCounts[i] > 0) {
                                    bytes32 numeratorKey;
                                    if (depositSizes[i] == 16 ether) {
                                        numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", _nodeAddress));
                                    } else {
                                        numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", _nodeAddress, depositSizes[i]));
                                    }
                                    uint256 numerator = getUint(numeratorKey);
                                    weightedAverage = weightedAverage + (numerator * depositWeights[i] / depositCounts[i]);
                                }
                            }
                            return weightedAverage / calcBase;
                        }
                        /// @notice Designates which network a node would like their rewards relayed to
                        function setRewardNetwork(address _nodeAddress, uint256 _network) override external onlyLatestContract("rocketNodeManager", address(this)) {
                            // Confirm the transaction is from the node's current withdrawal address
                            address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                            require(withdrawalAddress == msg.sender, "Only a tx from a node's withdrawal address can change reward network");
                            // Check network is enabled
                            RocketDAONodeTrustedSettingsRewardsInterface rocketDAONodeTrustedSettingsRewards = RocketDAONodeTrustedSettingsRewardsInterface(getContractAddress("rocketDAONodeTrustedSettingsRewards"));
                            require(rocketDAONodeTrustedSettingsRewards.getNetworkEnabled(_network), "Network is not enabled");
                            // Set the network
                            setUint(keccak256(abi.encodePacked("node.reward.network", _nodeAddress)), _network);
                            // Emit event
                            emit NodeRewardNetworkChanged(_nodeAddress, _network);
                        }
                        /// @notice Returns which network a node has designated as their desired reward network
                        function getRewardNetwork(address _nodeAddress) override public view onlyLatestContract("rocketNodeManager", address(this)) returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("node.reward.network", _nodeAddress)));
                        }
                        /// @notice Allows a node to register or deregister from the smoothing pool
                        function setSmoothingPoolRegistrationState(bool _state) override external onlyLatestContract("rocketNodeManager", address(this)) onlyRegisteredNode(msg.sender) {
                            // Ensure registration is enabled
                            RocketDAOProtocolSettingsNodeInterface daoSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            require(daoSettingsNode.getSmoothingPoolRegistrationEnabled(), "Smoothing pool registrations are not active");
                            // Precompute storage keys
                            bytes32 changeKey = keccak256(abi.encodePacked("node.smoothing.pool.changed.time", msg.sender));
                            bytes32 stateKey = keccak256(abi.encodePacked("node.smoothing.pool.state", msg.sender));
                            // Get from the DAO settings
                            RocketDAOProtocolSettingsRewardsInterface daoSettingsRewards = RocketDAOProtocolSettingsRewardsInterface(getContractAddress("rocketDAOProtocolSettingsRewards"));
                            uint256 rewardInterval = daoSettingsRewards.getRewardsClaimIntervalTime();
                            // Ensure node operator has waited the required time
                            uint256 lastChange = getUint(changeKey);
                            require(block.timestamp >= lastChange + rewardInterval, "Not enough time has passed since changing state");
                            // Ensure state is actually changing
                            require(getBool(stateKey) != _state, "Invalid state change");
                            // Update registration state
                            setUint(changeKey, block.timestamp);
                            setBool(stateKey, _state);
                            // Emit state change event
                            emit NodeSmoothingPoolStateChanged(msg.sender, _state);
                        }
                        /// @notice Returns whether a node is registered or not from the smoothing pool
                        function getSmoothingPoolRegistrationState(address _nodeAddress) override public view returns (bool) {
                            return getBool(keccak256(abi.encodePacked("node.smoothing.pool.state", _nodeAddress)));
                        }
                        /// @notice Returns the timestamp of when the node last changed their smoothing pool registration state
                        function getSmoothingPoolRegistrationChanged(address _nodeAddress) override external view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("node.smoothing.pool.changed.time", _nodeAddress)));
                        }
                        /// @notice Returns the sum of nodes that are registered for the smoothing pool between _offset and (_offset + _limit)
                        function getSmoothingPoolRegisteredNodeCount(uint256 _offset, uint256 _limit) override external view returns (uint256) {
                            // Get contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Precompute node key
                            bytes32 nodeKey = keccak256(abi.encodePacked("nodes.index"));
                            // Iterate over the requested minipool range
                            uint256 totalNodes = getNodeCount();
                            uint256 max = _offset + _limit;
                            if (max > totalNodes || _limit == 0) { max = totalNodes; }
                            uint256 count = 0;
                            for (uint256 i = _offset; i < max; ++i) {
                                address nodeAddress = addressSetStorage.getItem(nodeKey, i);
                                if (getSmoothingPoolRegistrationState(nodeAddress)) {
                                    count++;
                                }
                            }
                            return count;
                        }
                        /// @notice Convenience function to return all on-chain details about a given node
                        /// @param _nodeAddress Address of the node to query details for
                        function getNodeDetails(address _nodeAddress) override public view returns (NodeDetails memory nodeDetails) {
                            // Get contracts
                            RocketNodeStakingInterface rocketNodeStaking = RocketNodeStakingInterface(getContractAddress("rocketNodeStaking"));
                            RocketNodeDepositInterface rocketNodeDeposit = RocketNodeDepositInterface(getContractAddress("rocketNodeDeposit"));
                            RocketNodeDistributorFactoryInterface rocketNodeDistributorFactory = RocketNodeDistributorFactoryInterface(getContractAddress("rocketNodeDistributorFactory"));
                            RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                            IERC20 rocketTokenRETH = IERC20(getContractAddress("rocketTokenRETH"));
                            IERC20 rocketTokenRPL = IERC20(getContractAddress("rocketTokenRPL"));
                            IERC20 rocketTokenRPLFixedSupply = IERC20(getContractAddress("rocketTokenRPLFixedSupply"));
                            // Node details
                            nodeDetails.nodeAddress = _nodeAddress;
                            nodeDetails.withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                            nodeDetails.pendingWithdrawalAddress = rocketStorage.getNodePendingWithdrawalAddress(_nodeAddress);
                            nodeDetails.exists = getNodeExists(_nodeAddress);
                            nodeDetails.registrationTime = getNodeRegistrationTime(_nodeAddress);
                            nodeDetails.timezoneLocation = getNodeTimezoneLocation(_nodeAddress);
                            nodeDetails.feeDistributorInitialised = getFeeDistributorInitialised(_nodeAddress);
                            nodeDetails.rewardNetwork = getRewardNetwork(_nodeAddress);
                            // Staking details
                            nodeDetails.rplStake = rocketNodeStaking.getNodeRPLStake(_nodeAddress);
                            nodeDetails.effectiveRPLStake = rocketNodeStaking.getNodeEffectiveRPLStake(_nodeAddress);
                            nodeDetails.minimumRPLStake = rocketNodeStaking.getNodeMinimumRPLStake(_nodeAddress);
                            nodeDetails.maximumRPLStake = rocketNodeStaking.getNodeMaximumRPLStake(_nodeAddress);
                            nodeDetails.ethMatched = rocketNodeStaking.getNodeETHMatched(_nodeAddress);
                            nodeDetails.ethMatchedLimit = rocketNodeStaking.getNodeETHMatchedLimit(_nodeAddress);
                            // Distributor details
                            nodeDetails.feeDistributorAddress = rocketNodeDistributorFactory.getProxyAddress(_nodeAddress);
                            uint256 distributorBalance = nodeDetails.feeDistributorAddress.balance;
                            RocketNodeDistributorInterface distributor = RocketNodeDistributorInterface(nodeDetails.feeDistributorAddress);
                            nodeDetails.distributorBalanceNodeETH = distributor.getNodeShare();
                            nodeDetails.distributorBalanceUserETH = distributorBalance - nodeDetails.distributorBalanceNodeETH;
                            // Minipool details
                            nodeDetails.minipoolCount = rocketMinipoolManager.getNodeMinipoolCount(_nodeAddress);
                            // Balance details
                            nodeDetails.balanceETH = _nodeAddress.balance;
                            nodeDetails.balanceRETH = rocketTokenRETH.balanceOf(_nodeAddress);
                            nodeDetails.balanceRPL = rocketTokenRPL.balanceOf(_nodeAddress);
                            nodeDetails.balanceOldRPL = rocketTokenRPLFixedSupply.balanceOf(_nodeAddress);
                            nodeDetails.depositCreditBalance = rocketNodeDeposit.getNodeDepositCredit(_nodeAddress);
                            // Return
                            return nodeDetails;
                        }
                        /// @notice Returns a slice of the node operator address set
                        /// @param _offset The starting point into the slice
                        /// @param _limit The maximum number of results to return
                        function getNodeAddresses(uint256 _offset, uint256 _limit) override external view returns (address[] memory) {
                            // Get contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Precompute node key
                            bytes32 nodeKey = keccak256(abi.encodePacked("nodes.index"));
                            // Iterate over the requested minipool range
                            uint256 totalNodes = getNodeCount();
                            uint256 max = _offset + _limit;
                            if (max > totalNodes || _limit == 0) { max = totalNodes; }
                            // Create array big enough for every minipool
                            address[] memory nodes = new address[](max - _offset);
                            uint256 total = 0;
                            for (uint256 i = _offset; i < max; ++i) {
                                nodes[total] = addressSetStorage.getItem(nodeKey, i);
                                total++;
                            }
                            // Dirty hack to cut unused elements off end of return value
                            assembly {
                                mstore(nodes, total)
                            }
                            return nodes;
                        }
                    }
                    

                    File 6 of 10: RocketNodeDeposit
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    /// @title Base settings / modifiers for each contract in Rocket Pool
                    /// @author David Rugendyke
                    abstract contract RocketBase {
                        // Calculate using this as the base
                        uint256 constant calcBase = 1 ether;
                        // Version of the contract
                        uint8 public version;
                        // The main storage contract where primary persistant storage is maintained
                        RocketStorageInterface rocketStorage = RocketStorageInterface(address(0));
                        /*** Modifiers **********************************************************/
                        /**
                        * @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
                        */
                        modifier onlyLatestNetworkContract() {
                            require(getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))), "Invalid or outdated network contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that doesn't match one of the supplied contract or is the latest version of that contract
                        */
                        modifier onlyLatestContract(string memory _contractName, address _contractAddress) {
                            require(_contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", _contractName))), "Invalid or outdated contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered node
                        */
                        modifier onlyRegisteredNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a trusted node DAO member
                        */
                        modifier onlyTrustedNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("dao.trustednodes.", "member", _nodeAddress))), "Invalid trusted node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered minipool
                        */
                        modifier onlyRegisteredMinipool(address _minipoolAddress) {
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress))), "Invalid minipool");
                            _;
                        }
                        
                        /**
                        * @dev Throws if called by any account other than a guardian account (temporary account allowed access to settings before DAO is fully enabled)
                        */
                        modifier onlyGuardian() {
                            require(msg.sender == rocketStorage.getGuardian(), "Account is not a temporary guardian");
                            _;
                        }
                        /*** Methods **********************************************************/
                        /// @dev Set the main Rocket Storage address
                        constructor(RocketStorageInterface _rocketStorageAddress) {
                            // Update the contract address
                            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
                        }
                        /// @dev Get the address of a network contract by name
                        function getContractAddress(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Check it
                            require(contractAddress != address(0x0), "Contract not found");
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)
                        function getContractAddressUnsafe(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the name of a network contract by address
                        function getContractName(address _contractAddress) internal view returns (string memory) {
                            // Get the contract name
                            string memory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
                            // Check it
                            require(bytes(contractName).length > 0, "Contract not found");
                            // Return
                            return contractName;
                        }
                        /// @dev Get revert error message from a .call method
                        function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return "Transaction reverted silently";
                            assembly {
                                // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string)); // All that remains is the revert string
                        }
                        /*** Rocket Storage Methods ****************************************/
                        // Note: Unused helpers have been removed to keep contract sizes down
                        /// @dev Storage get methods
                        function getAddress(bytes32 _key) internal view returns (address) { return rocketStorage.getAddress(_key); }
                        function getUint(bytes32 _key) internal view returns (uint) { return rocketStorage.getUint(_key); }
                        function getString(bytes32 _key) internal view returns (string memory) { return rocketStorage.getString(_key); }
                        function getBytes(bytes32 _key) internal view returns (bytes memory) { return rocketStorage.getBytes(_key); }
                        function getBool(bytes32 _key) internal view returns (bool) { return rocketStorage.getBool(_key); }
                        function getInt(bytes32 _key) internal view returns (int) { return rocketStorage.getInt(_key); }
                        function getBytes32(bytes32 _key) internal view returns (bytes32) { return rocketStorage.getBytes32(_key); }
                        /// @dev Storage set methods
                        function setAddress(bytes32 _key, address _value) internal { rocketStorage.setAddress(_key, _value); }
                        function setUint(bytes32 _key, uint _value) internal { rocketStorage.setUint(_key, _value); }
                        function setString(bytes32 _key, string memory _value) internal { rocketStorage.setString(_key, _value); }
                        function setBytes(bytes32 _key, bytes memory _value) internal { rocketStorage.setBytes(_key, _value); }
                        function setBool(bytes32 _key, bool _value) internal { rocketStorage.setBool(_key, _value); }
                        function setInt(bytes32 _key, int _value) internal { rocketStorage.setInt(_key, _value); }
                        function setBytes32(bytes32 _key, bytes32 _value) internal { rocketStorage.setBytes32(_key, _value); }
                        /// @dev Storage delete methods
                        function deleteAddress(bytes32 _key) internal { rocketStorage.deleteAddress(_key); }
                        function deleteUint(bytes32 _key) internal { rocketStorage.deleteUint(_key); }
                        function deleteString(bytes32 _key) internal { rocketStorage.deleteString(_key); }
                        function deleteBytes(bytes32 _key) internal { rocketStorage.deleteBytes(_key); }
                        function deleteBool(bytes32 _key) internal { rocketStorage.deleteBool(_key); }
                        function deleteInt(bytes32 _key) internal { rocketStorage.deleteInt(_key); }
                        function deleteBytes32(bytes32 _key) internal { rocketStorage.deleteBytes32(_key); }
                        /// @dev Storage arithmetic methods
                        function addUint(bytes32 _key, uint256 _amount) internal { rocketStorage.addUint(_key, _amount); }
                        function subUint(bytes32 _key, uint256 _amount) internal { rocketStorage.subUint(_key, _amount); }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDepositPoolInterface {
                        function getBalance() external view returns (uint256);
                        function getNodeBalance() external view returns (uint256);
                        function getUserBalance() external view returns (int256);
                        function getExcessBalance() external view returns (uint256);
                        function deposit() external payable;
                        function getMaximumDepositAmount() external view returns (uint256);
                        function nodeDeposit(uint256 _totalAmount) external payable;
                        function nodeCreditWithdrawal(uint256 _amount) external;
                        function recycleDissolvedDeposit() external payable;
                        function recycleExcessCollateral() external payable;
                        function recycleLiquidatedStake() external payable;
                        function assignDeposits() external;
                        function maybeAssignDeposits() external returns (bool);
                        function withdrawExcessBalance(uint256 _amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents the type of deposits required by a minipool
                    enum MinipoolDeposit {
                        None,       // Marks an invalid deposit type
                        Full,       // The minipool requires 32 ETH from the node operator, 16 ETH of which will be refinanced from user deposits
                        Half,       // The minipool required 16 ETH from the node operator to be matched with 16 ETH from user deposits
                        Empty,      // The minipool requires 0 ETH from the node operator to be matched with 32 ETH from user deposits (trusted nodes only)
                        Variable    // Indicates this minipool is of the new generation that supports a variable deposit amount
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents a minipool's status within the network
                    enum MinipoolStatus {
                        Initialised,    // The minipool has been initialised and is awaiting a deposit of user ETH
                        Prelaunch,      // The minipool has enough ETH to begin staking and is awaiting launch by the node operator
                        Staking,        // The minipool is currently staking
                        Withdrawable,   // NO LONGER USED
                        Dissolved       // The minipool has been dissolved and its user deposited ETH has been returned to the deposit pool
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolStatus.sol";
                    import "../RocketStorageInterface.sol";
                    interface RocketMinipoolInterface {
                        function version() external view returns (uint8);
                        function initialise(address _nodeAddress) external;
                        function getStatus() external view returns (MinipoolStatus);
                        function getFinalised() external view returns (bool);
                        function getStatusBlock() external view returns (uint256);
                        function getStatusTime() external view returns (uint256);
                        function getScrubVoted(address _member) external view returns (bool);
                        function getDepositType() external view returns (MinipoolDeposit);
                        function getNodeAddress() external view returns (address);
                        function getNodeFee() external view returns (uint256);
                        function getNodeDepositBalance() external view returns (uint256);
                        function getNodeRefundBalance() external view returns (uint256);
                        function getNodeDepositAssigned() external view returns (bool);
                        function getPreLaunchValue() external view returns (uint256);
                        function getNodeTopUpValue() external view returns (uint256);
                        function getVacant() external view returns (bool);
                        function getPreMigrationBalance() external view returns (uint256);
                        function getUserDistributed() external view returns (bool);
                        function getUserDepositBalance() external view returns (uint256);
                        function getUserDepositAssigned() external view returns (bool);
                        function getUserDepositAssignedTime() external view returns (uint256);
                        function getTotalScrubVotes() external view returns (uint256);
                        function calculateNodeShare(uint256 _balance) external view returns (uint256);
                        function calculateUserShare(uint256 _balance) external view returns (uint256);
                        function preDeposit(uint256 _bondingValue, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot) external payable;
                        function deposit() external payable;
                        function userDeposit() external payable;
                        function distributeBalance(bool _rewardsOnly) external;
                        function beginUserDistribute() external;
                        function userDistributeAllowed() external view returns (bool);
                        function refund() external;
                        function slash() external;
                        function finalise() external;
                        function canStake() external view returns (bool);
                        function canPromote() external view returns (bool);
                        function stake(bytes calldata _validatorSignature, bytes32 _depositDataRoot) external;
                        function prepareVacancy(uint256 _bondAmount, uint256 _currentBalance) external;
                        function promote() external;
                        function dissolve() external;
                        function close() external;
                        function voteScrub() external;
                        function reduceBondAmount() external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "./MinipoolDeposit.sol";
                    import "./MinipoolStatus.sol";
                    // A struct containing all the information on-chain about a specific minipool
                    struct MinipoolDetails {
                        bool exists;
                        address minipoolAddress;
                        bytes pubkey;
                        MinipoolStatus status;
                        uint256 statusBlock;
                        uint256 statusTime;
                        bool finalised;
                        MinipoolDeposit depositType;
                        uint256 nodeFee;
                        uint256 nodeDepositBalance;
                        bool nodeDepositAssigned;
                        uint256 userDepositBalance;
                        bool userDepositAssigned;
                        uint256 userDepositAssignedTime;
                        bool useLatestDelegate;
                        address delegate;
                        address previousDelegate;
                        address effectiveDelegate;
                        uint256 penaltyCount;
                        uint256 penaltyRate;
                        address nodeAddress;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolDetails.sol";
                    import "./RocketMinipoolInterface.sol";
                    interface RocketMinipoolManagerInterface {
                        function getMinipoolCount() external view returns (uint256);
                        function getStakingMinipoolCount() external view returns (uint256);
                        function getFinalisedMinipoolCount() external view returns (uint256);
                        function getActiveMinipoolCount() external view returns (uint256);
                        function getMinipoolRPLSlashed(address _minipoolAddress) external view returns (bool);
                        function getMinipoolCountPerStatus(uint256 offset, uint256 limit) external view returns (uint256, uint256, uint256, uint256, uint256);
                        function getPrelaunchMinipools(uint256 offset, uint256 limit) external view returns (address[] memory);
                        function getMinipoolAt(uint256 _index) external view returns (address);
                        function getNodeMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeActiveMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeFinalisedMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeStakingMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeStakingMinipoolCountBySize(address _nodeAddress, uint256 _depositSize) external view returns (uint256);
                        function getNodeMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address);
                        function getNodeValidatingMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeValidatingMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address);
                        function getMinipoolByPubkey(bytes calldata _pubkey) external view returns (address);
                        function getMinipoolExists(address _minipoolAddress) external view returns (bool);
                        function getMinipoolDestroyed(address _minipoolAddress) external view returns (bool);
                        function getMinipoolPubkey(address _minipoolAddress) external view returns (bytes memory);
                        function updateNodeStakingMinipoolCount(uint256 _previousBond, uint256 _newBond, uint256 _previousFee, uint256 _newFee) external;
                        function getMinipoolWithdrawalCredentials(address _minipoolAddress) external pure returns (bytes memory);
                        function createMinipool(address _nodeAddress, uint256 _salt) external returns (RocketMinipoolInterface);
                        function createVacantMinipool(address _nodeAddress, uint256 _salt, bytes calldata _validatorPubkey, uint256 _bondAmount, uint256 _currentBalance) external returns (RocketMinipoolInterface);
                        function removeVacantMinipool() external;
                        function getVacantMinipoolCount() external view returns (uint256);
                        function getVacantMinipoolAt(uint256 _index) external view returns (address);
                        function destroyMinipool() external;
                        function incrementNodeStakingMinipoolCount(address _nodeAddress) external;
                        function decrementNodeStakingMinipoolCount(address _nodeAddress) external;
                        function tryDistribute(address _nodeAddress) external;
                        function incrementNodeFinalisedMinipoolCount(address _nodeAddress) external;
                        function setMinipoolPubkey(bytes calldata _pubkey) external;
                        function getMinipoolDepositType(address _minipoolAddress) external view returns (MinipoolDeposit);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    interface RocketMinipoolQueueInterface {
                        function getTotalLength() external view returns (uint256);
                        function getContainsLegacy() external view returns (bool);
                        function getLengthLegacy(MinipoolDeposit _depositType) external view returns (uint256);
                        function getLength() external view returns (uint256);
                        function getTotalCapacity() external view returns (uint256);
                        function getEffectiveCapacity() external view returns (uint256);
                        function getNextCapacityLegacy() external view returns (uint256);
                        function getNextDepositLegacy() external view returns (MinipoolDeposit, uint256);
                        function enqueueMinipool(address _minipool) external;
                        function dequeueMinipoolByDepositLegacy(MinipoolDeposit _depositType) external returns (address minipoolAddress);
                        function dequeueMinipools(uint256 _maxToDequeue) external returns (address[] memory minipoolAddress);
                        function removeMinipool(MinipoolDeposit _depositType) external;
                        function getMinipoolAt(uint256 _index) external view returns(address);
                        function getMinipoolPosition(address _minipool) external view returns (int256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNetworkFeesInterface {
                        function getNodeDemand() external view returns (int256);
                        function getNodeFee() external view returns (uint256);
                        function getNodeFeeByDemand(int256 _nodeDemand) external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    interface RocketNodeDepositInterface {
                        function getNodeDepositCredit(address _nodeAddress) external view returns (uint256);
                        function getNodeEthBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeCreditAndBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeUsableCreditAndBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeUsableCredit(address _nodeAddress) external view returns (uint256);
                        function increaseDepositCreditBalance(address _nodeOperator, uint256 _amount) external;
                        function depositEthFor(address _nodeAddress) external payable;
                        function withdrawEth(address _nodeAddress, uint256 _amount) external;
                        function deposit(uint256 _depositAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) external payable;
                        function depositWithCredit(uint256 _depositAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) external payable;
                        function isValidDepositAmount(uint256 _amount) external pure returns (bool);
                        function getDepositAmounts() external pure returns (uint256[] memory);
                        function createVacantMinipool(uint256 _bondAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, uint256 _salt, address _expectedMinipoolAddress, uint256 _currentBalance) external;
                        function increaseEthMatched(address _nodeAddress, uint256 _amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAOProtocolSettingsDepositInterface {
                        function getDepositEnabled() external view returns (bool);
                        function getAssignDepositsEnabled() external view returns (bool);
                        function getMinimumDeposit() external view returns (uint256);
                        function getMaximumDepositPoolSize() external view returns (uint256);
                        function getMaximumDepositAssignments() external view returns (uint256);
                        function getMaximumDepositSocialisedAssignments() external view returns (uint256);
                        function getDepositFee() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../../../types/MinipoolDeposit.sol";
                    interface RocketDAOProtocolSettingsMinipoolInterface {
                        function getLaunchBalance() external view returns (uint256);
                        function getPreLaunchValue() external pure returns (uint256);
                        function getDepositUserAmount(MinipoolDeposit _depositType) external view returns (uint256);
                        function getFullDepositUserAmount() external view returns (uint256);
                        function getHalfDepositUserAmount() external view returns (uint256);
                        function getVariableDepositAmount() external view returns (uint256);
                        function getSubmitWithdrawableEnabled() external view returns (bool);
                        function getBondReductionEnabled() external view returns (bool);
                        function getLaunchTimeout() external view returns (uint256);
                        function getMaximumCount() external view returns (uint256);
                        function isWithinUserDistributeWindow(uint256 _time) external view returns (bool);
                        function hasUserDistributeWindowPassed(uint256 _time) external view returns (bool);
                        function getUserDistributeWindowStart() external view returns (uint256);
                        function getUserDistributeWindowLength() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAOProtocolSettingsNodeInterface {
                        function getRegistrationEnabled() external view returns (bool);
                        function getSmoothingPoolRegistrationEnabled() external view returns (bool);
                        function getDepositEnabled() external view returns (bool);
                        function getVacantMinipoolsEnabled() external view returns (bool);
                        function getMinimumPerMinipoolStake() external view returns (uint256);
                        function getMaximumPerMinipoolStake() external view returns (uint256);
                        function getMaximumStakeForVotingPower() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAOProtocolSettingsNetworkInterface {
                        function getNodeConsensusThreshold() external view returns (uint256);
                        function getNodePenaltyThreshold() external view returns (uint256);
                        function getPerPenaltyRate() external view returns (uint256);
                        function getSubmitBalancesEnabled() external view returns (bool);
                        function getSubmitBalancesFrequency() external view returns (uint256);
                        function getSubmitPricesEnabled() external view returns (bool);
                        function getSubmitPricesFrequency() external view returns (uint256);
                        function getMinimumNodeFee() external view returns (uint256);
                        function getTargetNodeFee() external view returns (uint256);
                        function getMaximumNodeFee() external view returns (uint256);
                        function getNodeFeeDemandRange() external view returns (uint256);
                        function getTargetRethCollateralRate() external view returns (uint256);
                        function getRethDepositDelay() external view returns (uint256);
                        function getSubmitRewardsEnabled() external view returns (bool);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAONodeTrustedInterface {
                        function getBootstrapModeDisabled() external view returns (bool);
                        function getMemberQuorumVotesRequired() external view returns (uint256);
                        function getMemberAt(uint256 _index) external view returns (address);
                        function getMemberCount() external view returns (uint256);
                        function getMemberMinRequired() external view returns (uint256);
                        function getMemberIsValid(address _nodeAddress) external view returns (bool);
                        function getMemberLastProposalTime(address _nodeAddress) external view returns (uint256);
                        function getMemberID(address _nodeAddress) external view returns (string memory);
                        function getMemberUrl(address _nodeAddress) external view returns (string memory);
                        function getMemberJoinedTime(address _nodeAddress) external view returns (uint256);
                        function getMemberProposalExecutedTime(string memory _proposalType, address _nodeAddress) external view returns (uint256);
                        function getMemberRPLBondAmount(address _nodeAddress) external view returns (uint256);
                        function getMemberIsChallenged(address _nodeAddress) external view returns (bool);
                        function getMemberUnbondedValidatorCount(address _nodeAddress) external view returns (uint256);
                        function incrementMemberUnbondedValidatorCount(address _nodeAddress) external;
                        function decrementMemberUnbondedValidatorCount(address _nodeAddress) external;
                        function bootstrapMember(string memory _id, string memory _url, address _nodeAddress) external;
                        function bootstrapSettingUint(string memory _settingContractName, string memory _settingPath, uint256 _value) external;
                        function bootstrapSettingBool(string memory _settingContractName, string memory _settingPath, bool _value) external;
                        function bootstrapUpgrade(string memory _type, string memory _name, string memory _contractAbi, address _contractAddress) external;
                        function bootstrapDisable(bool _confirmDisableBootstrapMode) external;
                        function memberJoinRequired(string memory _id, string memory _url) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAONodeTrustedSettingsMembersInterface {
                        function getQuorum() external view returns (uint256);
                        function getRPLBond() external view returns(uint256);
                        function getMinipoolUnbondedMax() external view returns(uint256);
                        function getMinipoolUnbondedMinFee() external view returns(uint256);
                        function getChallengeCooldown() external view returns(uint256);
                        function getChallengeWindow() external view returns(uint256);
                        function getChallengeCost() external view returns(uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // A struct containing all the information on-chain about a specific node
                    struct NodeDetails {
                        bool exists;
                        uint256 registrationTime;
                        string timezoneLocation;
                        bool feeDistributorInitialised;
                        address feeDistributorAddress;
                        uint256 rewardNetwork;
                        uint256 rplStake;
                        uint256 effectiveRPLStake;
                        uint256 minimumRPLStake;
                        uint256 maximumRPLStake;
                        uint256 ethMatched;
                        uint256 ethMatchedLimit;
                        uint256 minipoolCount;
                        uint256 balanceETH;
                        uint256 balanceRETH;
                        uint256 balanceRPL;
                        uint256 balanceOldRPL;
                        uint256 depositCreditBalance;
                        uint256 distributorBalanceUserETH;
                        uint256 distributorBalanceNodeETH;
                        address withdrawalAddress;
                        address pendingWithdrawalAddress;
                        bool smoothingPoolRegistrationState;
                        uint256 smoothingPoolRegistrationChanged;
                        address nodeAddress;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    import "../../types/NodeDetails.sol";
                    interface RocketNodeManagerInterface {
                        // Structs
                        struct TimezoneCount {
                            string timezone;
                            uint256 count;
                        }
                        function getNodeCount() external view returns (uint256);
                        function getNodeCountPerTimezone(uint256 offset, uint256 limit) external view returns (TimezoneCount[] memory);
                        function getNodeAt(uint256 _index) external view returns (address);
                        function getNodeExists(address _nodeAddress) external view returns (bool);
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeRPLWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeRPLWithdrawalAddressIsSet(address _nodeAddress) external view returns (bool);
                        function unsetRPLWithdrawalAddress(address _nodeAddress) external;
                        function setRPLWithdrawalAddress(address _nodeAddress, address _newRPLWithdrawalAddress, bool _confirm) external;
                        function confirmRPLWithdrawalAddress(address _nodeAddress) external;
                        function getNodePendingRPLWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeTimezoneLocation(address _nodeAddress) external view returns (string memory);
                        function registerNode(string calldata _timezoneLocation) external;
                        function getNodeRegistrationTime(address _nodeAddress) external view returns (uint256);
                        function setTimezoneLocation(string calldata _timezoneLocation) external;
                        function setRewardNetwork(address _nodeAddress, uint256 network) external;
                        function getRewardNetwork(address _nodeAddress) external view returns (uint256);
                        function getFeeDistributorInitialised(address _nodeAddress) external view returns (bool);
                        function initialiseFeeDistributor() external;
                        function getAverageNodeFee(address _nodeAddress) external view returns (uint256);
                        function setSmoothingPoolRegistrationState(bool _state) external;
                        function getSmoothingPoolRegistrationState(address _nodeAddress) external returns (bool);
                        function getSmoothingPoolRegistrationChanged(address _nodeAddress) external returns (uint256);
                        function getSmoothingPoolRegisteredNodeCount(uint256 _offset, uint256 _limit) external view returns (uint256);
                        function getNodeDetails(address _nodeAddress) external view returns (NodeDetails memory);
                        function getNodeAddresses(uint256 _offset, uint256 _limit) external view returns (address[] memory);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                    pragma solidity >0.5.0 <0.9.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);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                    import "./IERC20.sol";
                    pragma solidity >0.5.0 <0.9.0;
                    interface IERC20Burnable is IERC20 {
                        function burn(uint256 amount) external;
                        function burnFrom(address account, uint256 amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "./util/IERC20Burnable.sol";
                    interface RocketVaultInterface {
                        function balanceOf(string memory _networkContractName) external view returns (uint256);
                        function depositEther() external payable;
                        function withdrawEther(uint256 _amount) external;
                        function depositToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external;
                        function withdrawToken(address _withdrawalAddress, IERC20 _tokenAddress, uint256 _amount) external;
                        function balanceOfToken(string memory _networkContractName, IERC20 _tokenAddress) external view returns (uint256);
                        function transferToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external;
                        function burnToken(IERC20Burnable _tokenAddress, uint256 _amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    interface RocketNodeStakingInterface {
                        function getTotalRPLStake() external view returns (uint256);
                        function getNodeRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatched(address _nodeAddress) external view returns (uint256);
                        function getNodeETHProvided(address _nodeAddress) external view returns (uint256);
                        function getNodeETHCollateralisationRatio(address _nodeAddress) external view returns (uint256);
                        function getNodeRPLStakedTime(address _nodeAddress) external view returns (uint256);
                        function getNodeEffectiveRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMinimumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMaximumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatchedLimit(address _nodeAddress) external view returns (uint256);
                        function getRPLLockingAllowed(address _nodeAddress) external view returns (bool);
                        function stakeRPL(uint256 _amount) external;
                        function stakeRPLFor(address _nodeAddress, uint256 _amount) external;
                        function setRPLLockingAllowed(address _nodeAddress, bool _allowed) external;
                        function setStakeRPLForAllowed(address _caller, bool _allowed) external;
                        function setStakeRPLForAllowed(address _nodeAddress, address _caller, bool _allowed) external;
                        function getNodeRPLLocked(address _nodeAddress) external view returns (uint256);
                        function lockRPL(address _nodeAddress, uint256 _amount) external;
                        function unlockRPL(address _nodeAddress, uint256 _amount) external;
                        function transferRPL(address _from, address _to, uint256 _amount) external;
                        function withdrawRPL(uint256 _amount) external;
                        function withdrawRPL(address _nodeAddress, uint256 _amount) external;
                        function slashRPL(address _nodeAddress, uint256 _ethSlashAmount) external;
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev Standard math utilities missing in the Solidity language.
                     */
                    library Math {
                        enum Rounding {
                            Down, // Toward negative infinity
                            Up, // Toward infinity
                            Zero // Toward zero
                        }
                        /**
                         * @dev Returns the largest of two numbers.
                         */
                        function max(uint256 a, uint256 b) internal pure returns (uint256) {
                            return a > b ? a : b;
                        }
                        /**
                         * @dev Returns the smallest of two numbers.
                         */
                        function min(uint256 a, uint256 b) internal pure returns (uint256) {
                            return a < b ? a : b;
                        }
                        /**
                         * @dev Returns the average of two numbers. The result is rounded towards
                         * zero.
                         */
                        function average(uint256 a, uint256 b) internal pure returns (uint256) {
                            // (a + b) / 2 can overflow.
                            return (a & b) + (a ^ b) / 2;
                        }
                        /**
                         * @dev Returns the ceiling of the division of two numbers.
                         *
                         * This differs from standard division with `/` in that it rounds up instead
                         * of rounding down.
                         */
                        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                            // (a + b - 1) / b can overflow on addition, so we distribute.
                            return a == 0 ? 0 : (a - 1) / b + 1;
                        }
                        /**
                         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                         * with further edits by Uniswap Labs also under MIT license.
                         */
                        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                            unchecked {
                                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                // variables such that product = prod1 * 2^256 + prod0.
                                uint256 prod0; // Least significant 256 bits of the product
                                uint256 prod1; // Most significant 256 bits of the product
                                assembly {
                                    let mm := mulmod(x, y, not(0))
                                    prod0 := mul(x, y)
                                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                }
                                // Handle non-overflow cases, 256 by 256 division.
                                if (prod1 == 0) {
                                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                                    // The surrounding unchecked block does not change this fact.
                                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                                    return prod0 / denominator;
                                }
                                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                require(denominator > prod1, "Math: mulDiv overflow");
                                ///////////////////////////////////////////////
                                // 512 by 256 division.
                                ///////////////////////////////////////////////
                                // Make division exact by subtracting the remainder from [prod1 prod0].
                                uint256 remainder;
                                assembly {
                                    // Compute remainder using mulmod.
                                    remainder := mulmod(x, y, denominator)
                                    // Subtract 256 bit number from 512 bit number.
                                    prod1 := sub(prod1, gt(remainder, prod0))
                                    prod0 := sub(prod0, remainder)
                                }
                                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                // See https://cs.stackexchange.com/q/138556/92363.
                                // Does not overflow because the denominator cannot be zero at this stage in the function.
                                uint256 twos = denominator & (~denominator + 1);
                                assembly {
                                    // Divide denominator by twos.
                                    denominator := div(denominator, twos)
                                    // Divide [prod1 prod0] by twos.
                                    prod0 := div(prod0, twos)
                                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                                    twos := add(div(sub(0, twos), twos), 1)
                                }
                                // Shift in bits from prod1 into prod0.
                                prod0 |= prod1 * twos;
                                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                // four bits. That is, denominator * inv = 1 mod 2^4.
                                uint256 inverse = (3 * denominator) ^ 2;
                                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                // in modular arithmetic, doubling the correct bits in each step.
                                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                // is no longer required.
                                result = prod0 * inverse;
                                return result;
                            }
                        }
                        /**
                         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                         */
                        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                            uint256 result = mulDiv(x, y, denominator);
                            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                                result += 1;
                            }
                            return result;
                        }
                        /**
                         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                         *
                         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                         */
                        function sqrt(uint256 a) internal pure returns (uint256) {
                            if (a == 0) {
                                return 0;
                            }
                            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                            //
                            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                            //
                            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                            //
                            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                            uint256 result = 1 << (log2(a) >> 1);
                            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                            // into the expected uint128 result.
                            unchecked {
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                return min(result, a / result);
                            }
                        }
                        /**
                         * @notice Calculates sqrt(a), following the selected rounding direction.
                         */
                        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = sqrt(a);
                                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 2, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log2(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >> 128 > 0) {
                                    value >>= 128;
                                    result += 128;
                                }
                                if (value >> 64 > 0) {
                                    value >>= 64;
                                    result += 64;
                                }
                                if (value >> 32 > 0) {
                                    value >>= 32;
                                    result += 32;
                                }
                                if (value >> 16 > 0) {
                                    value >>= 16;
                                    result += 16;
                                }
                                if (value >> 8 > 0) {
                                    value >>= 8;
                                    result += 8;
                                }
                                if (value >> 4 > 0) {
                                    value >>= 4;
                                    result += 4;
                                }
                                if (value >> 2 > 0) {
                                    value >>= 2;
                                    result += 2;
                                }
                                if (value >> 1 > 0) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log2(value);
                                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 10, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log10(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >= 10 ** 64) {
                                    value /= 10 ** 64;
                                    result += 64;
                                }
                                if (value >= 10 ** 32) {
                                    value /= 10 ** 32;
                                    result += 32;
                                }
                                if (value >= 10 ** 16) {
                                    value /= 10 ** 16;
                                    result += 16;
                                }
                                if (value >= 10 ** 8) {
                                    value /= 10 ** 8;
                                    result += 8;
                                }
                                if (value >= 10 ** 4) {
                                    value /= 10 ** 4;
                                    result += 4;
                                }
                                if (value >= 10 ** 2) {
                                    value /= 10 ** 2;
                                    result += 2;
                                }
                                if (value >= 10 ** 1) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log10(value);
                                return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 256, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         *
                         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                         */
                        function log256(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >> 128 > 0) {
                                    value >>= 128;
                                    result += 16;
                                }
                                if (value >> 64 > 0) {
                                    value >>= 64;
                                    result += 8;
                                }
                                if (value >> 32 > 0) {
                                    value >>= 32;
                                    result += 4;
                                }
                                if (value >> 16 > 0) {
                                    value >>= 16;
                                    result += 2;
                                }
                                if (value >> 8 > 0) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log256(value);
                                return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                            }
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    struct Checkpoint224 {
                        uint32 _block;
                        uint224 _value;
                    }
                    /// @notice Accounting for snapshotting of values based on block numbers
                    interface RocketNetworkSnapshotsInterface {
                        function push(bytes32 _key, uint224 _value) external;
                        function length(bytes32 _key) external view returns (uint256);
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224);
                        function latestBlock(bytes32 _key) external view returns (uint32);
                        function latestValue(bytes32 _key) external view returns (uint224);
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224);
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // Copyright (c) 2016-2023 zOS Global Limited and contributors
                    // Adapted from OpenZeppelin `Checkpoints` contract
                    pragma solidity 0.8.18;
                    import "@openzeppelin4/contracts/utils/math/Math.sol";
                    import "../RocketBase.sol";
                    import "../../interface/network/RocketNetworkSnapshotsInterface.sol";
                    /// @notice Accounting for snapshotting of values based on block numbers
                    contract RocketNetworkSnapshots is RocketBase, RocketNetworkSnapshotsInterface {
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            // Set contract version
                            version = 1;
                        }
                        function push(bytes32 _key, uint224 _value) onlyLatestContract("rocketNetworkSnapshots", address(this)) onlyLatestNetworkContract external {
                            _insert(_key, _value);
                        }
                        function length(bytes32 _key) public view returns (uint256) {
                            return rocketStorage.getUint(keccak256(abi.encodePacked("snapshot.length", _key)));
                        }
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224) {
                            uint256 len = length(_key);
                            if (len == 0) {
                                return (false, 0, 0);
                            }
                            Checkpoint224 memory checkpoint = _load(_key, len - 1);
                            return (true, checkpoint._block, checkpoint._value);
                        }
                        function latestBlock(bytes32 _key) external view returns (uint32) {
                            uint256 len = length(_key);
                            return len == 0 ? 0 : _blockAt(_key, len - 1);
                        }
                        function latestValue(bytes32 _key) external view returns (uint224) {
                            uint256 len = length(_key);
                            return len == 0 ? 0 : _valueAt(_key, len - 1);
                        }
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224) {
                            uint256 len = length(_key);
                            uint256 pos = _binaryLookup(_key, _block, 0, len);
                            return pos == 0 ? 0 : _valueAt(_key, pos - 1);
                        }
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224) {
                            uint256 len = length(_key);
                            uint256 low = 0;
                            uint256 high = len;
                            if (len > 5 && len > _recency) {
                                uint256 mid = len - _recency;
                                if (_block < _blockAt(_key, mid)) {
                                    high = mid;
                                } else {
                                    low = mid + 1;
                                }
                            }
                            uint256 pos = _binaryLookup(_key, _block, low, high);
                            return pos == 0 ? 0 : _valueAt(_key, pos - 1);
                        }
                        function _insert(bytes32 _key, uint224 _value) private {
                            uint32 blockNumber = uint32(block.number);
                            uint256 pos = length(_key);
                            if (pos > 0) {
                                Checkpoint224 memory last = _load(_key, pos - 1);
                                // Checkpoint keys must be non-decreasing.
                                require (last._block <= blockNumber, "Unordered snapshot insertion");
                                // Update or push new checkpoint
                                if (last._block == blockNumber) {
                                    last._value = _value;
                                    _set(_key, pos - 1, last);
                                } else {
                                    _push(_key, Checkpoint224({_block: blockNumber, _value: _value}));
                                }
                            } else {
                                _push(_key, Checkpoint224({_block: blockNumber, _value: _value}));
                            }
                        }
                        function _binaryLookup(
                            bytes32 _key,
                            uint32 _block,
                            uint256 _low,
                            uint256 _high
                        ) private view returns (uint256) {
                            while (_low < _high) {
                                uint256 mid = Math.average(_low, _high);
                                if (_blockAt(_key, mid) > _block) {
                                    _high = mid;
                                } else {
                                    _low = mid + 1;
                                }
                            }
                            return _high;
                        }
                        function _load(bytes32 _key, uint256 _pos) private view returns (Checkpoint224 memory) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            Checkpoint224 memory result;
                            result._block = uint32(uint256(raw) >> 224);
                            result._value = uint224(uint256(raw));
                            return result;
                        }
                        function _blockAt(bytes32 _key, uint256 _pos) private view returns (uint32) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            return uint32(uint256(raw) >> 224);
                        }
                        function _valueAt(bytes32 _key, uint256 _pos) private view returns (uint224) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            return uint224(uint256(raw));
                        }
                        function _push(bytes32 _key, Checkpoint224 memory _item) private {
                            bytes32 lengthKey = keccak256(abi.encodePacked("snapshot.length", _key));
                            uint256 snapshotLength = rocketStorage.getUint(lengthKey);
                            bytes32 key = bytes32(uint256(_key) + snapshotLength);
                            rocketStorage.setUint(lengthKey, snapshotLength + 1);
                            rocketStorage.setBytes32(key, _encode(_item));
                        }
                        function _set(bytes32 _key, uint256 _pos, Checkpoint224 memory _item) private {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            rocketStorage.setBytes32(key, _encode(_item));
                        }
                        function _encode(Checkpoint224 memory _item) private pure returns (bytes32) {
                            return bytes32(
                                uint256(_item._block) << 224 | uint256(_item._value)
                            );
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketVaultWithdrawerInterface {
                        function receiveVaultWithdrawalETH() external payable; 
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity 0.8.18;
                    import "../RocketBase.sol";
                    import "../../interface/deposit/RocketDepositPoolInterface.sol";
                    import "../../interface/minipool/RocketMinipoolInterface.sol";
                    import "../../interface/minipool/RocketMinipoolManagerInterface.sol";
                    import "../../interface/minipool/RocketMinipoolQueueInterface.sol";
                    import "../../interface/network/RocketNetworkFeesInterface.sol";
                    import "../../interface/node/RocketNodeDepositInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsDepositInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNetworkInterface.sol";
                    import "../../interface/dao/node/RocketDAONodeTrustedInterface.sol";
                    import "../../interface/dao/node/settings/RocketDAONodeTrustedSettingsMembersInterface.sol";
                    import "../../types/MinipoolDeposit.sol";
                    import "../../interface/node/RocketNodeManagerInterface.sol";
                    import "../../interface/RocketVaultInterface.sol";
                    import "../../interface/node/RocketNodeStakingInterface.sol";
                    import "../network/RocketNetworkSnapshots.sol";
                    import "../../interface/RocketVaultWithdrawerInterface.sol";
                    /// @notice Handles node deposits and minipool creation
                    contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface, RocketVaultWithdrawerInterface {
                        // Events
                        event DepositReceived(address indexed from, uint256 amount, uint256 time);
                        event DepositFor(address indexed nodeAddress, address indexed from, uint256 amount, uint256 time);
                        event Withdrawal(address indexed nodeAddress, address indexed to, uint256 amount, uint256 time);
                        function receiveVaultWithdrawalETH() external payable {}
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            version = 4;
                        }
                        /// @dev Accept incoming ETH from the deposit pool
                        receive() external payable onlyLatestContract("rocketDepositPool", msg.sender) {}
                        /// @notice Returns a node operator's credit balance in wei
                        function getNodeDepositCredit(address _nodeOperator) override public view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("node.deposit.credit.balance", _nodeOperator)));
                        }
                        /// @notice Returns the current ETH balance for the given node operator
                        function getNodeEthBalance(address _nodeAddress) override public view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("node.eth.balance", _nodeAddress)));
                        }
                        /// @notice Returns the sum of the credit balance of a given node operator and their balance
                        function getNodeCreditAndBalance(address _nodeAddress) override external view returns (uint256) {
                            return getNodeDepositCredit(_nodeAddress) + getNodeEthBalance(_nodeAddress);
                        }
                        /// @notice Returns the sum of the amount of ETH credit currently usable by a given node operator and their balance
                        function getNodeUsableCreditAndBalance(address _nodeAddress) override external view returns (uint256) {
                            return getNodeUsableCredit(_nodeAddress) + getNodeEthBalance(_nodeAddress);
                        }
                        /// @notice Returns the amount of ETH credit currently usable by a given node operator
                        function getNodeUsableCredit(address _nodeAddress) override public view returns (uint256) {
                            RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
                            uint256 depositPoolBalance = rocketDepositPool.getBalance();
                            uint256 usableCredit = getNodeDepositCredit(_nodeAddress);
                            if (usableCredit > depositPoolBalance) {
                                usableCredit = depositPoolBalance;
                            }
                            return usableCredit;
                        }
                        /// @dev Increases a node operators deposit credit balance
                        function increaseDepositCreditBalance(address _nodeAddress, uint256 _amount) override external onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(_nodeAddress) {
                            // Accept calls from network contracts or registered minipools
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", msg.sender))) ||
                                getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))),
                                "Invalid or outdated network contract");
                            // Increase credit balance
                            addUint(keccak256(abi.encodePacked("node.deposit.credit.balance", _nodeAddress)), _amount);
                        }
                        /// @notice Deposits ETH for the given node operator
                        /// @param _nodeAddress The address of the node operator to deposit ETH for
                        function depositEthFor(address _nodeAddress) override external payable onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(_nodeAddress) {
                            // Sanity check caller is not node itself
                            require(msg.sender != _nodeAddress, "Cannot deposit ETH for self");
                            // Send the ETH to vault
                            uint256 amount = msg.value;
                            RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
                            rocketVault.depositEther{value: amount}();
                            // Increment balance
                            addUint(keccak256(abi.encodePacked("node.eth.balance", _nodeAddress)), amount);
                            // Log it
                            emit DepositFor(_nodeAddress, msg.sender, amount, block.timestamp);
                        }
                        /// @notice Withdraws ETH from a node operator's balance. Must be called from withdrawal address.
                        /// @param _nodeAddress Address of the node operator to withdraw from
                        /// @param _amount Amount of ETH to withdraw
                        function withdrawEth(address _nodeAddress, uint256 _amount) external onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(_nodeAddress) {
                            // Check valid caller
                            address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                            require(msg.sender == withdrawalAddress, "Only withdrawal address can withdraw ETH");
                            // Check balance and update
                            uint256 balance = getNodeEthBalance(_nodeAddress);
                            require(balance >= _amount, "Insufficient balance");
                            setUint(keccak256(abi.encodePacked("node.eth.balance", _nodeAddress)), balance - _amount);
                            // Withdraw the funds
                            RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
                            rocketVault.withdrawEther(_amount);
                            // Send funds to withdrawalAddress
                            (bool success, ) = withdrawalAddress.call{value: _amount}("");
                            require(success, "Failed to withdraw ETH");
                            // Log it
                            emit Withdrawal(_nodeAddress, withdrawalAddress, _amount, block.timestamp);
                        }
                        /// @notice Accept a node deposit and create a new minipool under the node. Only accepts calls from registered nodes
                        /// @param _bondAmount The amount of capital the node operator wants to put up as his bond
                        /// @param _minimumNodeFee Transaction will revert if network commission rate drops below this amount
                        /// @param _validatorPubkey Pubkey of the validator the node operator wishes to migrate
                        /// @param _validatorSignature Signature from the validator over the deposit data
                        /// @param _depositDataRoot The hash tree root of the deposit data (passed onto the deposit contract on pre stake)
                        /// @param _salt Salt used to deterministically construct the minipool's address
                        /// @param _expectedMinipoolAddress The expected deterministic minipool address. Will revert if it doesn't match
                        function deposit(uint256 _bondAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) override external payable onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(msg.sender) {
                            // Check amount
                            require(msg.value == _bondAmount, "Invalid value");
                            // Process the deposit
                            _deposit(_bondAmount, _minimumNodeFee, _validatorPubkey, _validatorSignature, _depositDataRoot, _salt, _expectedMinipoolAddress);
                        }
                        /// @notice Accept a node deposit and create a new minipool under the node. Uses node's credit balance to cover
                        ///         shortfall in value provided to cover bond. Only accepts calls from registered nodes
                        /// @param _bondAmount The amount of capital the node operator wants to put up as his bond
                        /// @param _minimumNodeFee Transaction will revert if network commission rate drops below this amount
                        /// @param _validatorPubkey Pubkey of the validator the node operator wishes to migrate
                        /// @param _validatorSignature Signature from the validator over the deposit data
                        /// @param _depositDataRoot The hash tree root of the deposit data (passed onto the deposit contract on pre stake)
                        /// @param _salt Salt used to deterministically construct the minipool's address
                        /// @param _expectedMinipoolAddress The expected deterministic minipool address. Will revert if it doesn't match
                        function depositWithCredit(uint256 _bondAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) override external payable onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(msg.sender) {
                            // Sanity check
                            require(msg.value <= _bondAmount, "Excessive value for requested bond");
                            {
                                uint256 balanceToUse = 0;
                                uint256 creditToUse = 0;
                                uint256 shortFall = _bondAmount - msg.value;
                                uint256 credit = getNodeUsableCredit(msg.sender);
                                uint256 balance = getNodeEthBalance(msg.sender);
                                // Check credit
                                require (credit + balance >= shortFall, "Insufficient credit");
                                // Calculate amounts to use
                                creditToUse = shortFall;
                                if (credit < shortFall) {
                                    balanceToUse = shortFall - credit;
                                    creditToUse = credit;
                                }
                                // Update balances
                                if (balanceToUse > 0) {
                                    subUint(keccak256(abi.encodePacked("node.eth.balance", msg.sender)), balanceToUse);
                                    // Withdraw the funds
                                    RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
                                    rocketVault.withdrawEther(balanceToUse);
                                }
                                if (creditToUse > 0) {
                                    subUint(keccak256(abi.encodePacked("node.deposit.credit.balance", msg.sender)), creditToUse);
                                }
                            }
                            // Process the deposit
                            _deposit(_bondAmount, _minimumNodeFee, _validatorPubkey, _validatorSignature, _depositDataRoot, _salt, _expectedMinipoolAddress);
                        }
                        /// @notice Returns true if the given amount is a valid deposit amount
                        function isValidDepositAmount(uint256 _amount) override public pure returns (bool) {
                            return _amount == 16 ether || _amount == 8 ether;
                        }
                        /// @notice Returns an array of valid deposit amounts
                        function getDepositAmounts() override external pure returns (uint256[] memory) {
                            uint256[] memory amounts = new uint256[](2);
                            amounts[0] = 16 ether;
                            amounts[1] = 8 ether;
                            return amounts;
                        }
                        /// @dev Internal logic to process a deposit
                        function _deposit(uint256 _bondAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) private {
                            // Check pre-conditions
                            checkDepositsEnabled();
                            checkDistributorInitialised();
                            checkNodeFee(_minimumNodeFee);
                            require(isValidDepositAmount(_bondAmount), "Invalid deposit amount");
                            // Get launch constants
                            uint256 launchAmount;
                            uint256 preLaunchValue;
                            {
                                RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
                                launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
                                preLaunchValue = rocketDAOProtocolSettingsMinipool.getPreLaunchValue();
                            }
                            // Emit deposit received event
                            emit DepositReceived(msg.sender, msg.value, block.timestamp);
                            // Increase ETH matched (used to calculate RPL collateral requirements)
                            _increaseEthMatched(msg.sender, launchAmount - _bondAmount);
                            // Create the minipool
                            RocketMinipoolInterface minipool = createMinipool(_salt, _expectedMinipoolAddress);
                            // Process node deposit
                            _processNodeDeposit(preLaunchValue, _bondAmount);
                            // Perform the pre deposit
                            minipool.preDeposit{value: preLaunchValue}(_bondAmount, _validatorPubkey, _validatorSignature, _depositDataRoot);
                            // Enqueue the minipool
                            enqueueMinipool(address(minipool));
                            // Assign deposits if enabled
                            assignDeposits();
                        }
                        /// @dev Processes a node deposit with the deposit pool. If user has not supplied full bond amount with the transaction
                        ///      the shortfall will be taken from their credit. Any excess ETH after prelaunch value is sent to minipool is
                        //       then deposited into the deposit pool
                        /// @param _preLaunchValue The prelaunch value (result of call to `RocketDAOProtocolSettingsMinipool.getPreLaunchValue()`
                        /// @param _bondAmount The bond amount for this deposit
                        function _processNodeDeposit(uint256 _preLaunchValue, uint256 _bondAmount) private {
                            // Get contracts
                            RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
                            // Retrieve ETH from deposit pool if required
                            uint256 shortFall = 0;
                            if (address(this).balance < _preLaunchValue) {
                                shortFall = _preLaunchValue - address(this).balance;
                                rocketDepositPool.nodeCreditWithdrawal(shortFall);
                            }
                            uint256 remaining = address(this).balance - _preLaunchValue;
                            // Deposit the left over value into the deposit pool
                            rocketDepositPool.nodeDeposit{value: remaining}(_bondAmount - _preLaunchValue);
                        }
                        /// @notice Creates a "vacant" minipool which a node operator can use to migrate a validator with a BLS withdrawal credential
                        /// @param _bondAmount The amount of capital the node operator wants to put up as his bond
                        /// @param _minimumNodeFee Transaction will revert if network commission rate drops below this amount
                        /// @param _validatorPubkey Pubkey of the validator the node operator wishes to migrate
                        /// @param _salt Salt used to deterministically construct the minipool's address
                        /// @param _expectedMinipoolAddress The expected deterministic minipool address. Will revert if it doesn't match
                        /// @param _currentBalance The current balance of the validator on the beaconchain (will be checked by oDAO and scrubbed if not correct)
                        function createVacantMinipool(uint256 _bondAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, uint256 _salt, address _expectedMinipoolAddress, uint256 _currentBalance) override external onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(msg.sender) {
                            // Check pre-conditions
                            checkVacantMinipoolsEnabled();
                            checkDistributorInitialised();
                            checkNodeFee(_minimumNodeFee);
                            require(isValidDepositAmount(_bondAmount), "Invalid deposit amount");
                            // Increase ETH matched (used to calculate RPL collateral requirements)
                            RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
                            uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
                            _increaseEthMatched(msg.sender, launchAmount - _bondAmount);
                            // Create the minipool
                            _createVacantMinipool(_salt, _validatorPubkey, _bondAmount, _expectedMinipoolAddress, _currentBalance);
                        }
                        /// @notice Called by minipools during bond reduction to increase the amount of ETH the node operator has
                        /// @param _nodeAddress The node operator's address to increase the ETH matched for
                        /// @param _amount The amount to increase the ETH matched
                        /// @dev Will revert if the new ETH matched amount exceeds the node operators limit
                        function increaseEthMatched(address _nodeAddress, uint256 _amount) override external onlyLatestContract("rocketNodeDeposit", address(this)) onlyLatestNetworkContract() {
                            // Try to distribute any existing rewards at the previous collateral rate
                            RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                            rocketMinipoolManager.tryDistribute(_nodeAddress);
                            // Increase ETH matched
                            _increaseEthMatched(_nodeAddress, _amount);
                        }
                        /// @dev Increases the amount of ETH that has been matched against a node operators bond. Reverts if it exceeds the
                        ///      collateralisation requirements of the network
                        function _increaseEthMatched(address _nodeAddress, uint256 _amount) private {
                            // Check amount doesn't exceed limits
                            RocketNodeStakingInterface rocketNodeStaking = RocketNodeStakingInterface(getContractAddress("rocketNodeStaking"));
                            RocketNetworkSnapshots rocketNetworkSnapshots = RocketNetworkSnapshots(getContractAddress("rocketNetworkSnapshots"));
                            uint256 ethMatched = rocketNodeStaking.getNodeETHMatched(_nodeAddress) + _amount;
                            require(
                                ethMatched <= rocketNodeStaking.getNodeETHMatchedLimit(_nodeAddress),
                                "ETH matched after deposit exceeds limit based on node RPL stake"
                            );
                            // Push the change to snapshot manager
                            bytes32 key = keccak256(abi.encodePacked("eth.matched.node.amount", _nodeAddress));
                            rocketNetworkSnapshots.push(key, uint224(ethMatched));
                        }
                        /// @dev Adds a minipool to the queue
                        function enqueueMinipool(address _minipoolAddress) private {
                            // Add minipool to queue
                            RocketMinipoolQueueInterface(getContractAddress("rocketMinipoolQueue")).enqueueMinipool(_minipoolAddress);
                        }
                        /// @dev Reverts if node operator has not initialised their fee distributor
                        function checkDistributorInitialised() private view {
                            // Check node has initialised their fee distributor
                            RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
                            require(rocketNodeManager.getFeeDistributorInitialised(msg.sender), "Fee distributor not initialised");
                        }
                        /// @dev Creates a minipool and returns an instance of it
                        /// @param _salt The salt used to determine the minipools address
                        /// @param _expectedMinipoolAddress The expected minipool address. Reverts if not correct
                        function createMinipool(uint256 _salt, address _expectedMinipoolAddress) private returns (RocketMinipoolInterface) {
                            // Load contracts
                            RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                            // Check minipool doesn't exist or previously exist
                            require(!rocketMinipoolManager.getMinipoolExists(_expectedMinipoolAddress) && !rocketMinipoolManager.getMinipoolDestroyed(_expectedMinipoolAddress), "Minipool already exists or was previously destroyed");
                            // Create minipool
                            RocketMinipoolInterface minipool = rocketMinipoolManager.createMinipool(msg.sender, _salt);
                            // Ensure minipool address matches expected
                            require(address(minipool) == _expectedMinipoolAddress, "Unexpected minipool address");
                            // Return
                            return minipool;
                        }
                        /// @dev Creates a vacant minipool and returns an instance of it
                        /// @param _salt The salt used to determine the minipools address
                        /// @param _validatorPubkey Pubkey of the validator owning this minipool
                        /// @param _bondAmount ETH value the node operator is putting up as capital for this minipool
                        /// @param _expectedMinipoolAddress The expected minipool address. Reverts if not correct
                        /// @param _currentBalance The current balance of the validator on the beaconchain (will be checked by oDAO and scrubbed if not correct)
                        function _createVacantMinipool(uint256 _salt, bytes calldata _validatorPubkey, uint256 _bondAmount, address _expectedMinipoolAddress, uint256 _currentBalance) private returns (RocketMinipoolInterface) {
                            // Load contracts
                            RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                            // Check minipool doesn't exist or previously exist
                            require(!rocketMinipoolManager.getMinipoolExists(_expectedMinipoolAddress) && !rocketMinipoolManager.getMinipoolDestroyed(_expectedMinipoolAddress), "Minipool already exists or was previously destroyed");
                            // Create minipool
                            RocketMinipoolInterface minipool = rocketMinipoolManager.createVacantMinipool(msg.sender, _salt, _validatorPubkey, _bondAmount, _currentBalance);
                            // Ensure minipool address matches expected
                            require(address(minipool) == _expectedMinipoolAddress, "Unexpected minipool address");
                            // Return
                            return minipool;
                        }
                        /// @dev Reverts if network node fee is below a minimum
                        /// @param _minimumNodeFee The minimum node fee required to not revert
                        function checkNodeFee(uint256 _minimumNodeFee) private view {
                            // Load contracts
                            RocketNetworkFeesInterface rocketNetworkFees = RocketNetworkFeesInterface(getContractAddress("rocketNetworkFees"));
                            // Check current node fee
                            uint256 nodeFee = rocketNetworkFees.getNodeFee();
                            require(nodeFee >= _minimumNodeFee, "Minimum node fee exceeds current network node fee");
                        }
                        /// @dev Reverts if deposits are not enabled
                        function checkDepositsEnabled() private view {
                            // Get contracts
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Check node settings
                            require(rocketDAOProtocolSettingsNode.getDepositEnabled(), "Node deposits are currently disabled");
                        }
                        /// @dev Reverts if vacant minipools are not enabled
                        function checkVacantMinipoolsEnabled() private view {
                            // Get contracts
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Check node settings
                            require(rocketDAOProtocolSettingsNode.getVacantMinipoolsEnabled(), "Vacant minipools are currently disabled");
                        }
                        /// @dev Executes an assignDeposits call on the deposit pool
                        function assignDeposits() private {
                            RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
                            rocketDepositPool.maybeAssignDeposits();
                        }
                    }
                    

                    File 7 of 10: RocketDAOProtocolSettingsMinipool
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    /// @title Base settings / modifiers for each contract in Rocket Pool
                    /// @author David Rugendyke
                    abstract contract RocketBase {
                        // Calculate using this as the base
                        uint256 constant calcBase = 1 ether;
                        // Version of the contract
                        uint8 public version;
                        // The main storage contract where primary persistant storage is maintained
                        RocketStorageInterface rocketStorage = RocketStorageInterface(address(0));
                        /*** Modifiers **********************************************************/
                        /**
                        * @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
                        */
                        modifier onlyLatestNetworkContract() {
                            require(getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))), "Invalid or outdated network contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that doesn't match one of the supplied contract or is the latest version of that contract
                        */
                        modifier onlyLatestContract(string memory _contractName, address _contractAddress) {
                            require(_contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", _contractName))), "Invalid or outdated contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered node
                        */
                        modifier onlyRegisteredNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a trusted node DAO member
                        */
                        modifier onlyTrustedNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("dao.trustednodes.", "member", _nodeAddress))), "Invalid trusted node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered minipool
                        */
                        modifier onlyRegisteredMinipool(address _minipoolAddress) {
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress))), "Invalid minipool");
                            _;
                        }
                        
                        /**
                        * @dev Throws if called by any account other than a guardian account (temporary account allowed access to settings before DAO is fully enabled)
                        */
                        modifier onlyGuardian() {
                            require(msg.sender == rocketStorage.getGuardian(), "Account is not a temporary guardian");
                            _;
                        }
                        /*** Methods **********************************************************/
                        /// @dev Set the main Rocket Storage address
                        constructor(RocketStorageInterface _rocketStorageAddress) {
                            // Update the contract address
                            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
                        }
                        /// @dev Get the address of a network contract by name
                        function getContractAddress(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Check it
                            require(contractAddress != address(0x0), "Contract not found");
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)
                        function getContractAddressUnsafe(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the name of a network contract by address
                        function getContractName(address _contractAddress) internal view returns (string memory) {
                            // Get the contract name
                            string memory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
                            // Check it
                            require(bytes(contractName).length > 0, "Contract not found");
                            // Return
                            return contractName;
                        }
                        /// @dev Get revert error message from a .call method
                        function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return "Transaction reverted silently";
                            assembly {
                                // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string)); // All that remains is the revert string
                        }
                        /*** Rocket Storage Methods ****************************************/
                        // Note: Unused helpers have been removed to keep contract sizes down
                        /// @dev Storage get methods
                        function getAddress(bytes32 _key) internal view returns (address) { return rocketStorage.getAddress(_key); }
                        function getUint(bytes32 _key) internal view returns (uint) { return rocketStorage.getUint(_key); }
                        function getString(bytes32 _key) internal view returns (string memory) { return rocketStorage.getString(_key); }
                        function getBytes(bytes32 _key) internal view returns (bytes memory) { return rocketStorage.getBytes(_key); }
                        function getBool(bytes32 _key) internal view returns (bool) { return rocketStorage.getBool(_key); }
                        function getInt(bytes32 _key) internal view returns (int) { return rocketStorage.getInt(_key); }
                        function getBytes32(bytes32 _key) internal view returns (bytes32) { return rocketStorage.getBytes32(_key); }
                        /// @dev Storage set methods
                        function setAddress(bytes32 _key, address _value) internal { rocketStorage.setAddress(_key, _value); }
                        function setUint(bytes32 _key, uint _value) internal { rocketStorage.setUint(_key, _value); }
                        function setString(bytes32 _key, string memory _value) internal { rocketStorage.setString(_key, _value); }
                        function setBytes(bytes32 _key, bytes memory _value) internal { rocketStorage.setBytes(_key, _value); }
                        function setBool(bytes32 _key, bool _value) internal { rocketStorage.setBool(_key, _value); }
                        function setInt(bytes32 _key, int _value) internal { rocketStorage.setInt(_key, _value); }
                        function setBytes32(bytes32 _key, bytes32 _value) internal { rocketStorage.setBytes32(_key, _value); }
                        /// @dev Storage delete methods
                        function deleteAddress(bytes32 _key) internal { rocketStorage.deleteAddress(_key); }
                        function deleteUint(bytes32 _key) internal { rocketStorage.deleteUint(_key); }
                        function deleteString(bytes32 _key) internal { rocketStorage.deleteString(_key); }
                        function deleteBytes(bytes32 _key) internal { rocketStorage.deleteBytes(_key); }
                        function deleteBool(bytes32 _key) internal { rocketStorage.deleteBool(_key); }
                        function deleteInt(bytes32 _key) internal { rocketStorage.deleteInt(_key); }
                        function deleteBytes32(bytes32 _key) internal { rocketStorage.deleteBytes32(_key); }
                        /// @dev Storage arithmetic methods
                        function addUint(bytes32 _key, uint256 _amount) internal { rocketStorage.addUint(_key, _amount); }
                        function subUint(bytes32 _key, uint256 _amount) internal { rocketStorage.subUint(_key, _amount); }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAOProtocolSettingsInterface {
                        function getSettingUint(string memory _settingPath) external view returns (uint256);
                        function setSettingUint(string memory _settingPath, uint256 _value) external;
                        function getSettingBool(string memory _settingPath) external view returns (bool);
                        function setSettingBool(string memory _settingPath, bool _value) external;
                        function getSettingAddress(string memory _settingPath) external view returns (address);
                        function setSettingAddress(string memory _settingPath, address _value) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    import "../../../RocketBase.sol";
                    import "../../../../interface/dao/protocol/settings/RocketDAOProtocolSettingsInterface.sol";
                    // Settings in RP which the DAO will have full control over
                    // This settings contract enables storage using setting paths with namespaces, rather than explicit set methods
                    abstract contract RocketDAOProtocolSettings is RocketBase, RocketDAOProtocolSettingsInterface {
                        // The namespace for a particular group of settings
                        bytes32 settingNameSpace;
                        // Only allow updating from the DAO proposals contract
                        modifier onlyDAOProtocolProposal() {
                            // If this contract has been initialised, only allow access from the proposals contract
                            if(getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) require(getContractAddress("rocketDAOProtocolProposals") == msg.sender, "Only DAO Protocol Proposals contract can update a setting");
                            _;
                        }
                        // Construct
                        constructor(RocketStorageInterface _rocketStorageAddress, string memory _settingNameSpace) RocketBase(_rocketStorageAddress) {
                            // Apply the setting namespace
                            settingNameSpace = keccak256(abi.encodePacked("dao.protocol.setting.", _settingNameSpace));
                        }
                        /*** Uints  ****************/
                        // A general method to return any setting given the setting path is correct, only accepts uints
                        function getSettingUint(string memory _settingPath) public view override returns (uint256) {
                            return getUint(keccak256(abi.encodePacked(settingNameSpace, _settingPath)));
                        } 
                        // Update a Uint setting, can only be executed by the DAO contract when a majority on a setting proposal has passed and been executed
                        function setSettingUint(string memory _settingPath, uint256 _value) virtual public override onlyDAOProtocolProposal {
                            // Update setting now
                            setUint(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value);
                        } 
                       
                        /*** Bools  ****************/
                        // A general method to return any setting given the setting path is correct, only accepts bools
                        function getSettingBool(string memory _settingPath) public view override returns (bool) {
                            return getBool(keccak256(abi.encodePacked(settingNameSpace, _settingPath)));
                        } 
                        // Update a setting, can only be executed by the DAO contract when a majority on a setting proposal has passed and been executed
                        function setSettingBool(string memory _settingPath, bool _value) virtual public override onlyDAOProtocolProposal {
                            // Update setting now
                            setBool(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value);
                        }
                        
                        /*** Addresses  ****************/
                        // A general method to return any setting given the setting path is correct, only accepts addresses
                        function getSettingAddress(string memory _settingPath) external view override returns (address) {
                            return getAddress(keccak256(abi.encodePacked(settingNameSpace, _settingPath)));
                        } 
                        // Update a setting, can only be executed by the DAO contract when a majority on a setting proposal has passed and been executed
                        function setSettingAddress(string memory _settingPath, address _value) virtual external override onlyDAOProtocolProposal {
                            // Update setting now
                            setAddress(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value);
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents the type of deposits required by a minipool
                    enum MinipoolDeposit {
                        None,       // Marks an invalid deposit type
                        Full,       // The minipool requires 32 ETH from the node operator, 16 ETH of which will be refinanced from user deposits
                        Half,       // The minipool required 16 ETH from the node operator to be matched with 16 ETH from user deposits
                        Empty,      // The minipool requires 0 ETH from the node operator to be matched with 32 ETH from user deposits (trusted nodes only)
                        Variable    // Indicates this minipool is of the new generation that supports a variable deposit amount
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../../../types/MinipoolDeposit.sol";
                    interface RocketDAOProtocolSettingsMinipoolInterface {
                        function getLaunchBalance() external view returns (uint256);
                        function getPreLaunchValue() external pure returns (uint256);
                        function getDepositUserAmount(MinipoolDeposit _depositType) external view returns (uint256);
                        function getFullDepositUserAmount() external view returns (uint256);
                        function getHalfDepositUserAmount() external view returns (uint256);
                        function getVariableDepositAmount() external view returns (uint256);
                        function getSubmitWithdrawableEnabled() external view returns (bool);
                        function getBondReductionEnabled() external view returns (bool);
                        function getLaunchTimeout() external view returns (uint256);
                        function getMaximumCount() external view returns (uint256);
                        function isWithinUserDistributeWindow(uint256 _time) external view returns (bool);
                        function hasUserDistributeWindowPassed(uint256 _time) external view returns (bool);
                        function getUserDistributeWindowStart() external view returns (uint256);
                        function getUserDistributeWindowLength() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAONodeTrustedSettingsMinipoolInterface {
                        function getScrubPeriod() external view returns(uint256);
                        function getPromotionScrubPeriod() external view returns(uint256);
                        function getScrubQuorum() external view returns(uint256);
                        function getCancelBondReductionQuorum() external view returns(uint256);
                        function getScrubPenaltyEnabled() external view returns(bool);
                        function isWithinBondReductionWindow(uint256 _time) external view returns (bool);
                        function getBondReductionWindowStart() external view returns (uint256);
                        function getBondReductionWindowLength() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity 0.8.18;
                    import "./RocketDAOProtocolSettings.sol";
                    import "../../../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
                    import "../../../../interface/dao/node/settings/RocketDAONodeTrustedSettingsMinipoolInterface.sol";
                    import "../../../../types/MinipoolDeposit.sol";
                    /// @notice Network minipool settings
                    contract RocketDAOProtocolSettingsMinipool is RocketDAOProtocolSettings, RocketDAOProtocolSettingsMinipoolInterface {
                        uint256 constant internal minipoolUserDistributeWindowStart = 90 days;
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketDAOProtocolSettings(_rocketStorageAddress, "minipool") {
                            version = 3;
                            // Initialize settings on deployment
                            if(!getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) {
                                // Apply settings
                                setSettingBool("minipool.submit.withdrawable.enabled", false);
                                setSettingBool("minipool.bond.reduction.enabled", false);
                                setSettingUint("minipool.launch.timeout", 72 hours);
                                setSettingUint("minipool.maximum.count", 14);
                                setSettingUint("minipool.user.distribute.window.length", 2 days);
                                // Settings initialised
                                setBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")), true);
                            }
                        }
                        /// @notice Update a setting, overrides inherited setting method with extra checks for this contract
                        /// @param _settingPath The path of the setting within this contract's namespace
                        /// @param _value The value to set it to
                        function setSettingUint(string memory _settingPath, uint256 _value) override public onlyDAOProtocolProposal {
                            // Some safety guards for certain settings
                            if(getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) {
                                bytes32 settingKey = keccak256(abi.encodePacked(_settingPath));
                                if(settingKey == keccak256(abi.encodePacked("minipool.launch.timeout"))) {
                                    RocketDAONodeTrustedSettingsMinipoolInterface rocketDAONodeTrustedSettingsMinipool = RocketDAONodeTrustedSettingsMinipoolInterface(getContractAddress("rocketDAONodeTrustedSettingsMinipool"));
                                    require(_value >= (rocketDAONodeTrustedSettingsMinipool.getScrubPeriod() + 1 hours), "Launch timeout must be greater than scrub period");
                                    // >= 12 hours (RPIP-33)
                                    require(_value >= 12 hours, "Launch timeout must be greater than 12 hours");
                                } 
                            }
                            // Update setting now
                            setUint(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value);
                        }
                        /// @notice Returns the balance required to launch minipool
                        function getLaunchBalance() override public pure returns (uint256) {
                            return 32 ether;
                        }
                        /// @notice Returns the value required to pre-launch a minipool
                        function getPreLaunchValue() override public pure returns (uint256) {
                            return 1 ether;
                        }
                        /// @notice Returns the deposit amount for a given deposit type (only used for legacy minipool types)
                        function getDepositUserAmount(MinipoolDeposit _depositType) override external pure returns (uint256) {
                            if (_depositType == MinipoolDeposit.Full) { return getFullDepositUserAmount(); }
                            if (_depositType == MinipoolDeposit.Half) { return getHalfDepositUserAmount(); }
                            return 0;
                        }
                        /// @notice Returns the user amount for a "Full" deposit minipool
                        function getFullDepositUserAmount() override public pure returns (uint256) {
                            return getLaunchBalance() / 2;
                        }
                        /// @notice Returns the user amount for a "Half" deposit minipool
                        function getHalfDepositUserAmount() override public pure returns (uint256) {
                            return getLaunchBalance() / 2;
                        }
                        /// @notice Returns the amount a "Variable" minipool requires to move to staking status
                        function getVariableDepositAmount() override public pure returns (uint256) {
                            return getLaunchBalance() - getPreLaunchValue();
                        }
                        /// @notice Submit minipool withdrawable events currently enabled (trusted nodes only)
                        function getSubmitWithdrawableEnabled() override external view returns (bool) {
                            return getSettingBool("minipool.submit.withdrawable.enabled");
                        }
                        /// @notice Returns true if bond reductions are currentl enabled
                        function getBondReductionEnabled() override external view returns (bool) {
                            return getSettingBool("minipool.bond.reduction.enabled");
                        }
                        /// @notice Returns the timeout period in seconds for prelaunch minipools to launch
                        function getLaunchTimeout() override external view returns (uint256) {
                            return getSettingUint("minipool.launch.timeout");
                        }
                        /// @notice Returns the maximum number of minipools allowed at one time
                        function getMaximumCount() override external view returns (uint256) {
                          return getSettingUint("minipool.maximum.count");
                        }
                        /// @notice Returns true if the given time is within the user distribute window
                        function isWithinUserDistributeWindow(uint256 _time) override external view returns (bool) {
                            uint256 start = getUserDistributeWindowStart();
                            uint256 length = getUserDistributeWindowLength();
                            return (_time >= start && _time < (start + length));
                        }
                        /// @notice Returns true if the given time has passed the distribute window
                        function hasUserDistributeWindowPassed(uint256 _time) override external view returns (bool) {
                            uint256 start = getUserDistributeWindowStart();
                            uint256 length = getUserDistributeWindowLength();
                            return _time >= start + length;
                        }
                        /// @notice Returns the start of the user distribute window
                        function getUserDistributeWindowStart() override public pure returns (uint256) {
                            return minipoolUserDistributeWindowStart;
                        }
                        /// @notice Returns the length of the user distribute window
                        function getUserDistributeWindowLength() override public view returns (uint256) {
                            return getSettingUint("minipool.user.distribute.window.length");
                        }
                    }
                    

                    File 8 of 10: RocketMinipoolManager
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    /// @title Base settings / modifiers for each contract in Rocket Pool
                    /// @author David Rugendyke
                    abstract contract RocketBase {
                        // Calculate using this as the base
                        uint256 constant calcBase = 1 ether;
                        // Version of the contract
                        uint8 public version;
                        // The main storage contract where primary persistant storage is maintained
                        RocketStorageInterface rocketStorage = RocketStorageInterface(address(0));
                        /*** Modifiers **********************************************************/
                        /**
                        * @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
                        */
                        modifier onlyLatestNetworkContract() {
                            require(getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))), "Invalid or outdated network contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that doesn't match one of the supplied contract or is the latest version of that contract
                        */
                        modifier onlyLatestContract(string memory _contractName, address _contractAddress) {
                            require(_contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", _contractName))), "Invalid or outdated contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered node
                        */
                        modifier onlyRegisteredNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a trusted node DAO member
                        */
                        modifier onlyTrustedNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("dao.trustednodes.", "member", _nodeAddress))), "Invalid trusted node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered minipool
                        */
                        modifier onlyRegisteredMinipool(address _minipoolAddress) {
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress))), "Invalid minipool");
                            _;
                        }
                        
                        /**
                        * @dev Throws if called by any account other than a guardian account (temporary account allowed access to settings before DAO is fully enabled)
                        */
                        modifier onlyGuardian() {
                            require(msg.sender == rocketStorage.getGuardian(), "Account is not a temporary guardian");
                            _;
                        }
                        /*** Methods **********************************************************/
                        /// @dev Set the main Rocket Storage address
                        constructor(RocketStorageInterface _rocketStorageAddress) {
                            // Update the contract address
                            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
                        }
                        /// @dev Get the address of a network contract by name
                        function getContractAddress(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Check it
                            require(contractAddress != address(0x0), "Contract not found");
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)
                        function getContractAddressUnsafe(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the name of a network contract by address
                        function getContractName(address _contractAddress) internal view returns (string memory) {
                            // Get the contract name
                            string memory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
                            // Check it
                            require(bytes(contractName).length > 0, "Contract not found");
                            // Return
                            return contractName;
                        }
                        /// @dev Get revert error message from a .call method
                        function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return "Transaction reverted silently";
                            assembly {
                                // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string)); // All that remains is the revert string
                        }
                        /*** Rocket Storage Methods ****************************************/
                        // Note: Unused helpers have been removed to keep contract sizes down
                        /// @dev Storage get methods
                        function getAddress(bytes32 _key) internal view returns (address) { return rocketStorage.getAddress(_key); }
                        function getUint(bytes32 _key) internal view returns (uint) { return rocketStorage.getUint(_key); }
                        function getString(bytes32 _key) internal view returns (string memory) { return rocketStorage.getString(_key); }
                        function getBytes(bytes32 _key) internal view returns (bytes memory) { return rocketStorage.getBytes(_key); }
                        function getBool(bytes32 _key) internal view returns (bool) { return rocketStorage.getBool(_key); }
                        function getInt(bytes32 _key) internal view returns (int) { return rocketStorage.getInt(_key); }
                        function getBytes32(bytes32 _key) internal view returns (bytes32) { return rocketStorage.getBytes32(_key); }
                        /// @dev Storage set methods
                        function setAddress(bytes32 _key, address _value) internal { rocketStorage.setAddress(_key, _value); }
                        function setUint(bytes32 _key, uint _value) internal { rocketStorage.setUint(_key, _value); }
                        function setString(bytes32 _key, string memory _value) internal { rocketStorage.setString(_key, _value); }
                        function setBytes(bytes32 _key, bytes memory _value) internal { rocketStorage.setBytes(_key, _value); }
                        function setBool(bytes32 _key, bool _value) internal { rocketStorage.setBool(_key, _value); }
                        function setInt(bytes32 _key, int _value) internal { rocketStorage.setInt(_key, _value); }
                        function setBytes32(bytes32 _key, bytes32 _value) internal { rocketStorage.setBytes32(_key, _value); }
                        /// @dev Storage delete methods
                        function deleteAddress(bytes32 _key) internal { rocketStorage.deleteAddress(_key); }
                        function deleteUint(bytes32 _key) internal { rocketStorage.deleteUint(_key); }
                        function deleteString(bytes32 _key) internal { rocketStorage.deleteString(_key); }
                        function deleteBytes(bytes32 _key) internal { rocketStorage.deleteBytes(_key); }
                        function deleteBool(bytes32 _key) internal { rocketStorage.deleteBool(_key); }
                        function deleteInt(bytes32 _key) internal { rocketStorage.deleteInt(_key); }
                        function deleteBytes32(bytes32 _key) internal { rocketStorage.deleteBytes32(_key); }
                        /// @dev Storage arithmetic methods
                        function addUint(bytes32 _key, uint256 _amount) internal { rocketStorage.addUint(_key, _amount); }
                        function subUint(bytes32 _key, uint256 _amount) internal { rocketStorage.subUint(_key, _amount); }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents a minipool's status within the network
                    enum MinipoolStatus {
                        Initialised,    // The minipool has been initialised and is awaiting a deposit of user ETH
                        Prelaunch,      // The minipool has enough ETH to begin staking and is awaiting launch by the node operator
                        Staking,        // The minipool is currently staking
                        Withdrawable,   // NO LONGER USED
                        Dissolved       // The minipool has been dissolved and its user deposited ETH has been returned to the deposit pool
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents the type of deposits required by a minipool
                    enum MinipoolDeposit {
                        None,       // Marks an invalid deposit type
                        Full,       // The minipool requires 32 ETH from the node operator, 16 ETH of which will be refinanced from user deposits
                        Half,       // The minipool required 16 ETH from the node operator to be matched with 16 ETH from user deposits
                        Empty,      // The minipool requires 0 ETH from the node operator to be matched with 32 ETH from user deposits (trusted nodes only)
                        Variable    // Indicates this minipool is of the new generation that supports a variable deposit amount
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "./MinipoolDeposit.sol";
                    import "./MinipoolStatus.sol";
                    // A struct containing all the information on-chain about a specific minipool
                    struct MinipoolDetails {
                        bool exists;
                        address minipoolAddress;
                        bytes pubkey;
                        MinipoolStatus status;
                        uint256 statusBlock;
                        uint256 statusTime;
                        bool finalised;
                        MinipoolDeposit depositType;
                        uint256 nodeFee;
                        uint256 nodeDepositBalance;
                        bool nodeDepositAssigned;
                        uint256 userDepositBalance;
                        bool userDepositAssigned;
                        uint256 userDepositAssignedTime;
                        bool useLatestDelegate;
                        address delegate;
                        address previousDelegate;
                        address effectiveDelegate;
                        uint256 penaltyCount;
                        uint256 penaltyRate;
                        address nodeAddress;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAONodeTrustedInterface {
                        function getBootstrapModeDisabled() external view returns (bool);
                        function getMemberQuorumVotesRequired() external view returns (uint256);
                        function getMemberAt(uint256 _index) external view returns (address);
                        function getMemberCount() external view returns (uint256);
                        function getMemberMinRequired() external view returns (uint256);
                        function getMemberIsValid(address _nodeAddress) external view returns (bool);
                        function getMemberLastProposalTime(address _nodeAddress) external view returns (uint256);
                        function getMemberID(address _nodeAddress) external view returns (string memory);
                        function getMemberUrl(address _nodeAddress) external view returns (string memory);
                        function getMemberJoinedTime(address _nodeAddress) external view returns (uint256);
                        function getMemberProposalExecutedTime(string memory _proposalType, address _nodeAddress) external view returns (uint256);
                        function getMemberRPLBondAmount(address _nodeAddress) external view returns (uint256);
                        function getMemberIsChallenged(address _nodeAddress) external view returns (bool);
                        function getMemberUnbondedValidatorCount(address _nodeAddress) external view returns (uint256);
                        function incrementMemberUnbondedValidatorCount(address _nodeAddress) external;
                        function decrementMemberUnbondedValidatorCount(address _nodeAddress) external;
                        function bootstrapMember(string memory _id, string memory _url, address _nodeAddress) external;
                        function bootstrapSettingUint(string memory _settingContractName, string memory _settingPath, uint256 _value) external;
                        function bootstrapSettingBool(string memory _settingContractName, string memory _settingPath, bool _value) external;
                        function bootstrapUpgrade(string memory _type, string memory _name, string memory _contractAbi, address _contractAddress) external;
                        function bootstrapDisable(bool _confirmDisableBootstrapMode) external;
                        function memberJoinRequired(string memory _id, string memory _url) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolStatus.sol";
                    import "../RocketStorageInterface.sol";
                    interface RocketMinipoolInterface {
                        function version() external view returns (uint8);
                        function initialise(address _nodeAddress) external;
                        function getStatus() external view returns (MinipoolStatus);
                        function getFinalised() external view returns (bool);
                        function getStatusBlock() external view returns (uint256);
                        function getStatusTime() external view returns (uint256);
                        function getScrubVoted(address _member) external view returns (bool);
                        function getDepositType() external view returns (MinipoolDeposit);
                        function getNodeAddress() external view returns (address);
                        function getNodeFee() external view returns (uint256);
                        function getNodeDepositBalance() external view returns (uint256);
                        function getNodeRefundBalance() external view returns (uint256);
                        function getNodeDepositAssigned() external view returns (bool);
                        function getPreLaunchValue() external view returns (uint256);
                        function getNodeTopUpValue() external view returns (uint256);
                        function getVacant() external view returns (bool);
                        function getPreMigrationBalance() external view returns (uint256);
                        function getUserDistributed() external view returns (bool);
                        function getUserDepositBalance() external view returns (uint256);
                        function getUserDepositAssigned() external view returns (bool);
                        function getUserDepositAssignedTime() external view returns (uint256);
                        function getTotalScrubVotes() external view returns (uint256);
                        function calculateNodeShare(uint256 _balance) external view returns (uint256);
                        function calculateUserShare(uint256 _balance) external view returns (uint256);
                        function preDeposit(uint256 _bondingValue, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot) external payable;
                        function deposit() external payable;
                        function userDeposit() external payable;
                        function distributeBalance(bool _rewardsOnly) external;
                        function beginUserDistribute() external;
                        function userDistributeAllowed() external view returns (bool);
                        function refund() external;
                        function slash() external;
                        function finalise() external;
                        function canStake() external view returns (bool);
                        function canPromote() external view returns (bool);
                        function stake(bytes calldata _validatorSignature, bytes32 _depositDataRoot) external;
                        function prepareVacancy(uint256 _bondAmount, uint256 _currentBalance) external;
                        function promote() external;
                        function dissolve() external;
                        function close() external;
                        function voteScrub() external;
                        function reduceBondAmount() external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolDetails.sol";
                    import "./RocketMinipoolInterface.sol";
                    interface RocketMinipoolManagerInterface {
                        function getMinipoolCount() external view returns (uint256);
                        function getStakingMinipoolCount() external view returns (uint256);
                        function getFinalisedMinipoolCount() external view returns (uint256);
                        function getActiveMinipoolCount() external view returns (uint256);
                        function getMinipoolRPLSlashed(address _minipoolAddress) external view returns (bool);
                        function getMinipoolCountPerStatus(uint256 offset, uint256 limit) external view returns (uint256, uint256, uint256, uint256, uint256);
                        function getPrelaunchMinipools(uint256 offset, uint256 limit) external view returns (address[] memory);
                        function getMinipoolAt(uint256 _index) external view returns (address);
                        function getNodeMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeActiveMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeFinalisedMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeStakingMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeStakingMinipoolCountBySize(address _nodeAddress, uint256 _depositSize) external view returns (uint256);
                        function getNodeMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address);
                        function getNodeValidatingMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeValidatingMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address);
                        function getMinipoolByPubkey(bytes calldata _pubkey) external view returns (address);
                        function getMinipoolExists(address _minipoolAddress) external view returns (bool);
                        function getMinipoolDestroyed(address _minipoolAddress) external view returns (bool);
                        function getMinipoolPubkey(address _minipoolAddress) external view returns (bytes memory);
                        function updateNodeStakingMinipoolCount(uint256 _previousBond, uint256 _newBond, uint256 _previousFee, uint256 _newFee) external;
                        function getMinipoolWithdrawalCredentials(address _minipoolAddress) external pure returns (bytes memory);
                        function createMinipool(address _nodeAddress, uint256 _salt) external returns (RocketMinipoolInterface);
                        function createVacantMinipool(address _nodeAddress, uint256 _salt, bytes calldata _validatorPubkey, uint256 _bondAmount, uint256 _currentBalance) external returns (RocketMinipoolInterface);
                        function removeVacantMinipool() external;
                        function getVacantMinipoolCount() external view returns (uint256);
                        function getVacantMinipoolAt(uint256 _index) external view returns (address);
                        function destroyMinipool() external;
                        function incrementNodeStakingMinipoolCount(address _nodeAddress) external;
                        function decrementNodeStakingMinipoolCount(address _nodeAddress) external;
                        function tryDistribute(address _nodeAddress) external;
                        function incrementNodeFinalisedMinipoolCount(address _nodeAddress) external;
                        function setMinipoolPubkey(bytes calldata _pubkey) external;
                        function getMinipoolDepositType(address _minipoolAddress) external view returns (MinipoolDeposit);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    interface RocketNodeStakingInterface {
                        function getTotalRPLStake() external view returns (uint256);
                        function getNodeRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatched(address _nodeAddress) external view returns (uint256);
                        function getNodeETHProvided(address _nodeAddress) external view returns (uint256);
                        function getNodeETHCollateralisationRatio(address _nodeAddress) external view returns (uint256);
                        function getNodeRPLStakedTime(address _nodeAddress) external view returns (uint256);
                        function getNodeEffectiveRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMinimumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMaximumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatchedLimit(address _nodeAddress) external view returns (uint256);
                        function getRPLLockingAllowed(address _nodeAddress) external view returns (bool);
                        function stakeRPL(uint256 _amount) external;
                        function stakeRPLFor(address _nodeAddress, uint256 _amount) external;
                        function setRPLLockingAllowed(address _nodeAddress, bool _allowed) external;
                        function setStakeRPLForAllowed(address _caller, bool _allowed) external;
                        function setStakeRPLForAllowed(address _nodeAddress, address _caller, bool _allowed) external;
                        function getNodeRPLLocked(address _nodeAddress) external view returns (uint256);
                        function lockRPL(address _nodeAddress, uint256 _amount) external;
                        function unlockRPL(address _nodeAddress, uint256 _amount) external;
                        function transferRPL(address _from, address _to, uint256 _amount) external;
                        function withdrawRPL(uint256 _amount) external;
                        function withdrawRPL(address _nodeAddress, uint256 _amount) external;
                        function slashRPL(address _nodeAddress, uint256 _ethSlashAmount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface AddressSetStorageInterface {
                        function getCount(bytes32 _key) external view returns (uint);
                        function getItem(bytes32 _key, uint _index) external view returns (address);
                        function getIndexOf(bytes32 _key, address _value) external view returns (int);
                        function addItem(bytes32 _key, address _value) external;
                        function removeItem(bytes32 _key, address _value) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // A struct containing all the information on-chain about a specific node
                    struct NodeDetails {
                        bool exists;
                        uint256 registrationTime;
                        string timezoneLocation;
                        bool feeDistributorInitialised;
                        address feeDistributorAddress;
                        uint256 rewardNetwork;
                        uint256 rplStake;
                        uint256 effectiveRPLStake;
                        uint256 minimumRPLStake;
                        uint256 maximumRPLStake;
                        uint256 ethMatched;
                        uint256 ethMatchedLimit;
                        uint256 minipoolCount;
                        uint256 balanceETH;
                        uint256 balanceRETH;
                        uint256 balanceRPL;
                        uint256 balanceOldRPL;
                        uint256 depositCreditBalance;
                        uint256 distributorBalanceUserETH;
                        uint256 distributorBalanceNodeETH;
                        address withdrawalAddress;
                        address pendingWithdrawalAddress;
                        bool smoothingPoolRegistrationState;
                        uint256 smoothingPoolRegistrationChanged;
                        address nodeAddress;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    import "../../types/NodeDetails.sol";
                    interface RocketNodeManagerInterface {
                        // Structs
                        struct TimezoneCount {
                            string timezone;
                            uint256 count;
                        }
                        function getNodeCount() external view returns (uint256);
                        function getNodeCountPerTimezone(uint256 offset, uint256 limit) external view returns (TimezoneCount[] memory);
                        function getNodeAt(uint256 _index) external view returns (address);
                        function getNodeExists(address _nodeAddress) external view returns (bool);
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeRPLWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeRPLWithdrawalAddressIsSet(address _nodeAddress) external view returns (bool);
                        function unsetRPLWithdrawalAddress(address _nodeAddress) external;
                        function setRPLWithdrawalAddress(address _nodeAddress, address _newRPLWithdrawalAddress, bool _confirm) external;
                        function confirmRPLWithdrawalAddress(address _nodeAddress) external;
                        function getNodePendingRPLWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeTimezoneLocation(address _nodeAddress) external view returns (string memory);
                        function registerNode(string calldata _timezoneLocation) external;
                        function getNodeRegistrationTime(address _nodeAddress) external view returns (uint256);
                        function setTimezoneLocation(string calldata _timezoneLocation) external;
                        function setRewardNetwork(address _nodeAddress, uint256 network) external;
                        function getRewardNetwork(address _nodeAddress) external view returns (uint256);
                        function getFeeDistributorInitialised(address _nodeAddress) external view returns (bool);
                        function initialiseFeeDistributor() external;
                        function getAverageNodeFee(address _nodeAddress) external view returns (uint256);
                        function setSmoothingPoolRegistrationState(bool _state) external;
                        function getSmoothingPoolRegistrationState(address _nodeAddress) external returns (bool);
                        function getSmoothingPoolRegistrationChanged(address _nodeAddress) external returns (uint256);
                        function getSmoothingPoolRegisteredNodeCount(uint256 _offset, uint256 _limit) external view returns (uint256);
                        function getNodeDetails(address _nodeAddress) external view returns (NodeDetails memory);
                        function getNodeAddresses(uint256 _offset, uint256 _limit) external view returns (address[] memory);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNetworkPricesInterface {
                        function getPricesBlock() external view returns (uint256);
                        function getRPLPrice() external view returns (uint256);
                        function submitPrices(uint256 _block, uint256 _slotTimestamp, uint256 _rplPrice) external;
                        function executeUpdatePrices(uint256 _block, uint256 _slotTimestamp, uint256 _rplPrice) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../../../types/MinipoolDeposit.sol";
                    interface RocketDAOProtocolSettingsMinipoolInterface {
                        function getLaunchBalance() external view returns (uint256);
                        function getPreLaunchValue() external pure returns (uint256);
                        function getDepositUserAmount(MinipoolDeposit _depositType) external view returns (uint256);
                        function getFullDepositUserAmount() external view returns (uint256);
                        function getHalfDepositUserAmount() external view returns (uint256);
                        function getVariableDepositAmount() external view returns (uint256);
                        function getSubmitWithdrawableEnabled() external view returns (bool);
                        function getBondReductionEnabled() external view returns (bool);
                        function getLaunchTimeout() external view returns (uint256);
                        function getMaximumCount() external view returns (uint256);
                        function isWithinUserDistributeWindow(uint256 _time) external view returns (bool);
                        function hasUserDistributeWindowPassed(uint256 _time) external view returns (bool);
                        function getUserDistributeWindowStart() external view returns (uint256);
                        function getUserDistributeWindowLength() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAOProtocolSettingsNodeInterface {
                        function getRegistrationEnabled() external view returns (bool);
                        function getSmoothingPoolRegistrationEnabled() external view returns (bool);
                        function getDepositEnabled() external view returns (bool);
                        function getVacantMinipoolsEnabled() external view returns (bool);
                        function getMinimumPerMinipoolStake() external view returns (uint256);
                        function getMaximumPerMinipoolStake() external view returns (uint256);
                        function getMaximumStakeForVotingPower() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    interface RocketMinipoolFactoryInterface {
                        function getExpectedAddress(address _nodeAddress, uint256 _salt) external view returns (address);
                        function deployContract(address _nodeAddress, uint256 _salt) external returns (address);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNodeDistributorFactoryInterface {
                        function getProxyBytecode() external pure returns (bytes memory);
                        function getProxyAddress(address _nodeAddress) external view returns(address);
                        function createProxy(address _nodeAddress) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNodeDistributorInterface {
                        function getNodeShare() external view returns (uint256);
                        function getUserShare() external view returns (uint256);
                        function distribute() external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNetworkPenaltiesInterface {
                        function submitPenalty(address _minipoolAddress, uint256 _block) external;
                        function executeUpdatePenalty(address _minipoolAddress, uint256 _block) external;
                        function getPenaltyCount(address _minipoolAddress) external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketMinipoolPenaltyInterface {
                        // Max penalty rate
                        function setMaxPenaltyRate(uint256 _rate) external;
                        function getMaxPenaltyRate() external view returns (uint256);
                        // Penalty rate
                        function setPenaltyRate(address _minipoolAddress, uint256 _rate) external;
                        function getPenaltyRate(address _minipoolAddress) external view returns(uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    interface RocketNodeDepositInterface {
                        function getNodeDepositCredit(address _nodeAddress) external view returns (uint256);
                        function getNodeEthBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeCreditAndBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeUsableCreditAndBalance(address _nodeAddress) external view returns (uint256);
                        function getNodeUsableCredit(address _nodeAddress) external view returns (uint256);
                        function increaseDepositCreditBalance(address _nodeOperator, uint256 _amount) external;
                        function depositEthFor(address _nodeAddress) external payable;
                        function withdrawEth(address _nodeAddress, uint256 _amount) external;
                        function deposit(uint256 _depositAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) external payable;
                        function depositWithCredit(uint256 _depositAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) external payable;
                        function isValidDepositAmount(uint256 _amount) external pure returns (bool);
                        function getDepositAmounts() external pure returns (uint256[] memory);
                        function createVacantMinipool(uint256 _bondAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, uint256 _salt, address _expectedMinipoolAddress, uint256 _currentBalance) external;
                        function increaseEthMatched(address _nodeAddress, uint256 _amount) external;
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev Standard math utilities missing in the Solidity language.
                     */
                    library Math {
                        enum Rounding {
                            Down, // Toward negative infinity
                            Up, // Toward infinity
                            Zero // Toward zero
                        }
                        /**
                         * @dev Returns the largest of two numbers.
                         */
                        function max(uint256 a, uint256 b) internal pure returns (uint256) {
                            return a > b ? a : b;
                        }
                        /**
                         * @dev Returns the smallest of two numbers.
                         */
                        function min(uint256 a, uint256 b) internal pure returns (uint256) {
                            return a < b ? a : b;
                        }
                        /**
                         * @dev Returns the average of two numbers. The result is rounded towards
                         * zero.
                         */
                        function average(uint256 a, uint256 b) internal pure returns (uint256) {
                            // (a + b) / 2 can overflow.
                            return (a & b) + (a ^ b) / 2;
                        }
                        /**
                         * @dev Returns the ceiling of the division of two numbers.
                         *
                         * This differs from standard division with `/` in that it rounds up instead
                         * of rounding down.
                         */
                        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                            // (a + b - 1) / b can overflow on addition, so we distribute.
                            return a == 0 ? 0 : (a - 1) / b + 1;
                        }
                        /**
                         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                         * with further edits by Uniswap Labs also under MIT license.
                         */
                        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                            unchecked {
                                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                // variables such that product = prod1 * 2^256 + prod0.
                                uint256 prod0; // Least significant 256 bits of the product
                                uint256 prod1; // Most significant 256 bits of the product
                                assembly {
                                    let mm := mulmod(x, y, not(0))
                                    prod0 := mul(x, y)
                                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                }
                                // Handle non-overflow cases, 256 by 256 division.
                                if (prod1 == 0) {
                                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                                    // The surrounding unchecked block does not change this fact.
                                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                                    return prod0 / denominator;
                                }
                                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                require(denominator > prod1, "Math: mulDiv overflow");
                                ///////////////////////////////////////////////
                                // 512 by 256 division.
                                ///////////////////////////////////////////////
                                // Make division exact by subtracting the remainder from [prod1 prod0].
                                uint256 remainder;
                                assembly {
                                    // Compute remainder using mulmod.
                                    remainder := mulmod(x, y, denominator)
                                    // Subtract 256 bit number from 512 bit number.
                                    prod1 := sub(prod1, gt(remainder, prod0))
                                    prod0 := sub(prod0, remainder)
                                }
                                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                // See https://cs.stackexchange.com/q/138556/92363.
                                // Does not overflow because the denominator cannot be zero at this stage in the function.
                                uint256 twos = denominator & (~denominator + 1);
                                assembly {
                                    // Divide denominator by twos.
                                    denominator := div(denominator, twos)
                                    // Divide [prod1 prod0] by twos.
                                    prod0 := div(prod0, twos)
                                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                                    twos := add(div(sub(0, twos), twos), 1)
                                }
                                // Shift in bits from prod1 into prod0.
                                prod0 |= prod1 * twos;
                                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                // four bits. That is, denominator * inv = 1 mod 2^4.
                                uint256 inverse = (3 * denominator) ^ 2;
                                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                // in modular arithmetic, doubling the correct bits in each step.
                                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                // is no longer required.
                                result = prod0 * inverse;
                                return result;
                            }
                        }
                        /**
                         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                         */
                        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                            uint256 result = mulDiv(x, y, denominator);
                            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                                result += 1;
                            }
                            return result;
                        }
                        /**
                         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                         *
                         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                         */
                        function sqrt(uint256 a) internal pure returns (uint256) {
                            if (a == 0) {
                                return 0;
                            }
                            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                            //
                            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                            //
                            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                            //
                            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                            uint256 result = 1 << (log2(a) >> 1);
                            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                            // into the expected uint128 result.
                            unchecked {
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                return min(result, a / result);
                            }
                        }
                        /**
                         * @notice Calculates sqrt(a), following the selected rounding direction.
                         */
                        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = sqrt(a);
                                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 2, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log2(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >> 128 > 0) {
                                    value >>= 128;
                                    result += 128;
                                }
                                if (value >> 64 > 0) {
                                    value >>= 64;
                                    result += 64;
                                }
                                if (value >> 32 > 0) {
                                    value >>= 32;
                                    result += 32;
                                }
                                if (value >> 16 > 0) {
                                    value >>= 16;
                                    result += 16;
                                }
                                if (value >> 8 > 0) {
                                    value >>= 8;
                                    result += 8;
                                }
                                if (value >> 4 > 0) {
                                    value >>= 4;
                                    result += 4;
                                }
                                if (value >> 2 > 0) {
                                    value >>= 2;
                                    result += 2;
                                }
                                if (value >> 1 > 0) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log2(value);
                                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 10, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log10(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >= 10 ** 64) {
                                    value /= 10 ** 64;
                                    result += 64;
                                }
                                if (value >= 10 ** 32) {
                                    value /= 10 ** 32;
                                    result += 32;
                                }
                                if (value >= 10 ** 16) {
                                    value /= 10 ** 16;
                                    result += 16;
                                }
                                if (value >= 10 ** 8) {
                                    value /= 10 ** 8;
                                    result += 8;
                                }
                                if (value >= 10 ** 4) {
                                    value /= 10 ** 4;
                                    result += 4;
                                }
                                if (value >= 10 ** 2) {
                                    value /= 10 ** 2;
                                    result += 2;
                                }
                                if (value >= 10 ** 1) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log10(value);
                                return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 256, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         *
                         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                         */
                        function log256(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >> 128 > 0) {
                                    value >>= 128;
                                    result += 16;
                                }
                                if (value >> 64 > 0) {
                                    value >>= 64;
                                    result += 8;
                                }
                                if (value >> 32 > 0) {
                                    value >>= 32;
                                    result += 4;
                                }
                                if (value >> 16 > 0) {
                                    value >>= 16;
                                    result += 2;
                                }
                                if (value >> 8 > 0) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log256(value);
                                return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                            }
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    struct Checkpoint224 {
                        uint32 _block;
                        uint224 _value;
                    }
                    /// @notice Accounting for snapshotting of values based on block numbers
                    interface RocketNetworkSnapshotsInterface {
                        function push(bytes32 _key, uint224 _value) external;
                        function length(bytes32 _key) external view returns (uint256);
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224);
                        function latestBlock(bytes32 _key) external view returns (uint32);
                        function latestValue(bytes32 _key) external view returns (uint224);
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224);
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // Copyright (c) 2016-2023 zOS Global Limited and contributors
                    // Adapted from OpenZeppelin `Checkpoints` contract
                    pragma solidity 0.8.18;
                    import "@openzeppelin4/contracts/utils/math/Math.sol";
                    import "../RocketBase.sol";
                    import "../../interface/network/RocketNetworkSnapshotsInterface.sol";
                    /// @notice Accounting for snapshotting of values based on block numbers
                    contract RocketNetworkSnapshots is RocketBase, RocketNetworkSnapshotsInterface {
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            // Set contract version
                            version = 1;
                        }
                        function push(bytes32 _key, uint224 _value) onlyLatestContract("rocketNetworkSnapshots", address(this)) onlyLatestNetworkContract external {
                            _insert(_key, _value);
                        }
                        function length(bytes32 _key) public view returns (uint256) {
                            return rocketStorage.getUint(keccak256(abi.encodePacked("snapshot.length", _key)));
                        }
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224) {
                            uint256 len = length(_key);
                            if (len == 0) {
                                return (false, 0, 0);
                            }
                            Checkpoint224 memory checkpoint = _load(_key, len - 1);
                            return (true, checkpoint._block, checkpoint._value);
                        }
                        function latestBlock(bytes32 _key) external view returns (uint32) {
                            uint256 len = length(_key);
                            return len == 0 ? 0 : _blockAt(_key, len - 1);
                        }
                        function latestValue(bytes32 _key) external view returns (uint224) {
                            uint256 len = length(_key);
                            return len == 0 ? 0 : _valueAt(_key, len - 1);
                        }
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224) {
                            uint256 len = length(_key);
                            uint256 pos = _binaryLookup(_key, _block, 0, len);
                            return pos == 0 ? 0 : _valueAt(_key, pos - 1);
                        }
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224) {
                            uint256 len = length(_key);
                            uint256 low = 0;
                            uint256 high = len;
                            if (len > 5 && len > _recency) {
                                uint256 mid = len - _recency;
                                if (_block < _blockAt(_key, mid)) {
                                    high = mid;
                                } else {
                                    low = mid + 1;
                                }
                            }
                            uint256 pos = _binaryLookup(_key, _block, low, high);
                            return pos == 0 ? 0 : _valueAt(_key, pos - 1);
                        }
                        function _insert(bytes32 _key, uint224 _value) private {
                            uint32 blockNumber = uint32(block.number);
                            uint256 pos = length(_key);
                            if (pos > 0) {
                                Checkpoint224 memory last = _load(_key, pos - 1);
                                // Checkpoint keys must be non-decreasing.
                                require (last._block <= blockNumber, "Unordered snapshot insertion");
                                // Update or push new checkpoint
                                if (last._block == blockNumber) {
                                    last._value = _value;
                                    _set(_key, pos - 1, last);
                                } else {
                                    _push(_key, Checkpoint224({_block: blockNumber, _value: _value}));
                                }
                            } else {
                                _push(_key, Checkpoint224({_block: blockNumber, _value: _value}));
                            }
                        }
                        function _binaryLookup(
                            bytes32 _key,
                            uint32 _block,
                            uint256 _low,
                            uint256 _high
                        ) private view returns (uint256) {
                            while (_low < _high) {
                                uint256 mid = Math.average(_low, _high);
                                if (_blockAt(_key, mid) > _block) {
                                    _high = mid;
                                } else {
                                    _low = mid + 1;
                                }
                            }
                            return _high;
                        }
                        function _load(bytes32 _key, uint256 _pos) private view returns (Checkpoint224 memory) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            Checkpoint224 memory result;
                            result._block = uint32(uint256(raw) >> 224);
                            result._value = uint224(uint256(raw));
                            return result;
                        }
                        function _blockAt(bytes32 _key, uint256 _pos) private view returns (uint32) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            return uint32(uint256(raw) >> 224);
                        }
                        function _valueAt(bytes32 _key, uint256 _pos) private view returns (uint224) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            return uint224(uint256(raw));
                        }
                        function _push(bytes32 _key, Checkpoint224 memory _item) private {
                            bytes32 lengthKey = keccak256(abi.encodePacked("snapshot.length", _key));
                            uint256 snapshotLength = rocketStorage.getUint(lengthKey);
                            bytes32 key = bytes32(uint256(_key) + snapshotLength);
                            rocketStorage.setUint(lengthKey, snapshotLength + 1);
                            rocketStorage.setBytes32(key, _encode(_item));
                        }
                        function _set(bytes32 _key, uint256 _pos, Checkpoint224 memory _item) private {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            rocketStorage.setBytes32(key, _encode(_item));
                        }
                        function _encode(Checkpoint224 memory _item) private pure returns (bytes32) {
                            return bytes32(
                                uint256(_item._block) << 224 | uint256(_item._value)
                            );
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                    pragma solidity >0.5.0 <0.9.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);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    interface RocketDAOProtocolSettingsRewardsInterface {
                        function setSettingRewardsClaimers(uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent) external;
                        function getRewardsClaimerPerc(string memory _contractName) external view returns (uint256);
                        function getRewardsClaimersPerc() external view returns (uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent);
                        function getRewardsClaimersTrustedNodePerc() external view returns (uint256);
                        function getRewardsClaimersProtocolPerc() external view returns (uint256);
                        function getRewardsClaimersNodePerc() external view returns (uint256);
                        function getRewardsClaimersTimeUpdated() external view returns (uint256);
                        function getRewardsClaimIntervalPeriods() external view returns (uint256);
                        function getRewardsClaimIntervalTime() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                    import "./IERC20.sol";
                    pragma solidity >0.5.0 <0.9.0;
                    interface IERC20Burnable is IERC20 {
                        function burn(uint256 amount) external;
                        function burnFrom(address account, uint256 amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "./util/IERC20Burnable.sol";
                    interface RocketVaultInterface {
                        function balanceOf(string memory _networkContractName) external view returns (uint256);
                        function depositEther() external payable;
                        function withdrawEther(uint256 _amount) external;
                        function depositToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external;
                        function withdrawToken(address _withdrawalAddress, IERC20 _tokenAddress, uint256 _amount) external;
                        function balanceOfToken(string memory _networkContractName, IERC20 _tokenAddress) external view returns (uint256);
                        function transferToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external;
                        function burnToken(IERC20Burnable _tokenAddress, uint256 _amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity 0.8.18;
                    import "../../interface/util/IERC20.sol";
                    import "../RocketBase.sol";
                    import "../../interface/minipool/RocketMinipoolManagerInterface.sol";
                    import "../../interface/network/RocketNetworkPricesInterface.sol";
                    import "../../interface/node/RocketNodeStakingInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsRewardsInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
                    import "../../interface/RocketVaultInterface.sol";
                    import "../../interface/util/AddressSetStorageInterface.sol";
                    import "../../interface/network/RocketNetworkSnapshotsInterface.sol";
                    import "../network/RocketNetworkSnapshots.sol";
                    import "../../interface/node/RocketNodeManagerInterface.sol";
                    /// @notice Handles node deposits and minipool creation
                    contract RocketNodeStaking is RocketBase, RocketNodeStakingInterface {
                        // Constants
                        bytes32 immutable internal totalKey;
                        // Events
                        event RPLStaked(address indexed from, uint256 amount, uint256 time);
                        event RPLWithdrawn(address indexed to, uint256 amount, uint256 time);
                        event RPLSlashed(address indexed node, uint256 amount, uint256 ethValue, uint256 time);
                        event StakeRPLForAllowed(address indexed node, address indexed caller, bool allowed, uint256 time);
                        event RPLLockingAllowed(address indexed node, bool allowed, uint256 time);
                        event RPLLocked(address indexed from, uint256 amount, uint256 time);
                        event RPLUnlocked(address indexed from, uint256 amount, uint256 time);
                        event RPLTransferred(address indexed from, address indexed to, uint256 amount, uint256 time);
                        modifier onlyRPLWithdrawalAddressOrNode(address _nodeAddress) {
                            // Check that the call is coming from RPL withdrawal address (or node if unset)
                            RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
                            if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
                                address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
                                require(msg.sender == rplWithdrawalAddress, "Must be called from RPL withdrawal address");
                            } else {
                                require(msg.sender == _nodeAddress, "Must be called from node address");
                            }
                            _;
                        }
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            version = 5;
                            // Precompute keys
                            totalKey = keccak256(abi.encodePacked("rpl.staked.total.amount"));
                        }
                        /// @notice Returns the total quantity of RPL staked on the network
                        function getTotalRPLStake() override external view returns (uint256) {
                            return getUint(totalKey);
                        }
                        /// @dev Increases the total network RPL stake
                        /// @param _amount How much to increase by
                        function increaseTotalRPLStake(uint256 _amount) private {
                            addUint(totalKey, _amount);
                        }
                        /// @dev Decrease the total network RPL stake
                        /// @param _amount How much to decrease by
                        function decreaseTotalRPLStake(uint256 _amount) private {
                            subUint(totalKey, _amount);
                        }
                        /// @notice Returns the amount a given node operator has staked
                        /// @param _nodeAddress The address of the node operator to query
                        function getNodeRPLStake(address _nodeAddress) override public view returns (uint256) {
                            bytes32 key = keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            (bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
                            uint256 stake = uint256(value);
                            if (!exists){
                                // Fallback to old value
                                stake = getUint(key);
                            }
                            return stake;
                        }
                        /// @dev Increases a node operator's RPL stake
                        /// @param _amount How much to increase by
                        function increaseNodeRPLStake(address _nodeAddress, uint256 _amount) private {
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            bytes32 key = keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
                            (bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
                            if (!exists){
                                value = uint224(getUint(key));
                            }
                            rocketNetworkSnapshots.push(key, value + uint224(_amount));
                        }
                        /// @dev Decrease a node operator's RPL stake
                        /// @param _amount How much to decrease by
                        function decreaseNodeRPLStake(address _nodeAddress, uint256 _amount) private {
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            bytes32 key = keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
                            (bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
                            if (!exists){
                                value = uint224(getUint(key));
                            }
                            rocketNetworkSnapshots.push(key, value - uint224(_amount));
                        }
                        /// @notice Returns a node's matched ETH amount (amount taken from protocol to stake)
                        /// @param _nodeAddress The address of the node operator to query
                        function getNodeETHMatched(address _nodeAddress) override public view returns (uint256) {
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            bytes32 key = keccak256(abi.encodePacked("eth.matched.node.amount", _nodeAddress));
                            (bool exists, , uint224 value) = rocketNetworkSnapshots.latest(key);
                            if (exists) {
                                // Value was previously set in a snapshot so return that
                                return value;
                            } else {
                                // Fallback to old method
                                uint256 ethMatched = getUint(key);
                                if (ethMatched > 0) {
                                    // Value was previously calculated and stored so return that
                                    return ethMatched;
                                } else {
                                    // Fallback for backwards compatibility before ETH matched was recorded (all legacy minipools matched 16 ETH from protocol)
                                    RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                                    return rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress) * 16 ether;
                                }
                            }
                        }
                        /// @notice Returns a node's provided ETH amount (amount supplied to create minipools)
                        /// @param _nodeAddress The address of the node operator to query
                        function getNodeETHProvided(address _nodeAddress) override public view returns (uint256) {
                            // Get contracts
                            RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                            uint256 activeMinipoolCount = rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress);
                            // Retrieve stored ETH matched value
                            uint256 ethMatched = getNodeETHMatched(_nodeAddress);
                            if (ethMatched > 0) {
                                RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
                                uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
                                // ETH provided is number of staking minipools * 32 - eth matched
                                uint256 totalEthStaked = activeMinipoolCount * launchAmount;
                                return totalEthStaked - ethMatched;
                            } else {
                                // Fallback for legacy minipools is number of staking minipools * 16
                                return activeMinipoolCount * 16 ether;
                            }
                        }
                        /// @notice Returns the ratio between capital taken from users and provided by a node operator.
                        ///         The value is a 1e18 precision fixed point integer value of (node capital + user capital) / node capital.
                        /// @param _nodeAddress The address of the node operator to query
                        function getNodeETHCollateralisationRatio(address _nodeAddress) override public view returns (uint256) {
                            uint256 ethMatched = getNodeETHMatched(_nodeAddress);
                            if (ethMatched == 0) {
                                // Node operator only has legacy minipools and all legacy minipools had a 1:1 ratio
                                return calcBase * 2;
                            } else {
                                RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
                                uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
                                RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                                uint256 totalEthStaked = rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress) * launchAmount;
                                return (totalEthStaked * calcBase) / (totalEthStaked - ethMatched);
                            }
                        }
                        /// @notice Returns the timestamp at which a node last staked RPL
                        function getNodeRPLStakedTime(address _nodeAddress) override public view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("rpl.staked.node.time", _nodeAddress)));
                        }
                        /// @dev Sets the timestamp at which a node last staked RPL
                        /// @param _nodeAddress The address of the node operator to set the value for
                        /// @param _time The timestamp to set
                        function setNodeRPLStakedTime(address _nodeAddress, uint256 _time) private {
                            setUint(keccak256(abi.encodePacked("rpl.staked.node.time", _nodeAddress)), _time);
                        }
                        /// @notice Calculate and return a node's effective RPL stake amount
                        /// @param _nodeAddress The address of the node operator to calculate for
                        function getNodeEffectiveRPLStake(address _nodeAddress) override public view returns (uint256) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Get node's current RPL stake
                            uint256 rplStake = getNodeRPLStake(_nodeAddress);
                            // Retrieve variables for calculations
                            uint256 matchedETH = getNodeETHMatched(_nodeAddress);
                            uint256 providedETH = getNodeETHProvided(_nodeAddress);
                            uint256 rplPrice = rocketNetworkPrices.getRPLPrice();
                            // RPL stake cannot exceed maximum
                            uint256 maximumStakePercent = rocketDAOProtocolSettingsNode.getMaximumPerMinipoolStake();
                            uint256 maximumStake = providedETH * maximumStakePercent / rplPrice;
                            if (rplStake > maximumStake) {
                                return maximumStake;
                            }
                            // If RPL stake is lower than minimum, node has no effective stake
                            uint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
                            uint256 minimumStake = matchedETH * minimumStakePercent / rplPrice;
                            if (rplStake < minimumStake) {
                                return 0;
                            }
                            // Otherwise, return the actual stake
                            return rplStake;
                        }
                        /// @notice Calculate and return a node's minimum RPL stake to collateralize their minipools
                        /// @param _nodeAddress The address of the node operator to calculate for
                        function getNodeMinimumRPLStake(address _nodeAddress) override external view returns (uint256) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Retrieve variables
                            uint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
                            uint256 matchedETH = getNodeETHMatched(_nodeAddress);
                            return matchedETH * minimumStakePercent / rocketNetworkPrices.getRPLPrice();
                        }
                        /// @notice Calculate and return a node's maximum RPL stake to fully collateralise their minipools
                        /// @param _nodeAddress The address of the node operator to calculate for
                        function getNodeMaximumRPLStake(address _nodeAddress) override public view returns (uint256) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Retrieve variables
                            uint256 maximumStakePercent = rocketDAOProtocolSettingsNode.getMaximumPerMinipoolStake();
                            uint256 providedETH = getNodeETHProvided(_nodeAddress);
                            return providedETH * maximumStakePercent / rocketNetworkPrices.getRPLPrice();
                        }
                        /// @notice Calculate and return a node's limit of how much user ETH they can use based on RPL stake
                        /// @param _nodeAddress The address of the node operator to calculate for
                        function getNodeETHMatchedLimit(address _nodeAddress) override external view returns (uint256) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Calculate & return limit
                            uint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
                            return getNodeRPLStake(_nodeAddress) *rocketNetworkPrices.getRPLPrice() / minimumStakePercent;
                        }
                        /// @notice Returns whether this node allows RPL locking or not
                        /// @param _nodeAddress The address of the node operator to query for
                        function getRPLLockingAllowed(address _nodeAddress) external view returns (bool) {
                            return getBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress)));
                        }
                        /// @notice Accept an RPL stake from the node operator's own address
                        ///         Requires the node's RPL withdrawal address to be unset
                        /// @param _amount The amount of RPL to stake
                        function stakeRPL(uint256 _amount) override external {
                            stakeRPLFor(msg.sender, _amount);
                        }
                        /// @notice Accept an RPL stake from any address for a specified node
                        ///         Requires caller to have approved this contract to spend RPL
                        ///         Requires caller to be on the node operator's allow list (see `setStakeForAllowed`)
                        /// @param _nodeAddress The address of the node operator to stake on behalf of
                        /// @param _amount The amount of RPL to stake
                        function stakeRPLFor(address _nodeAddress, uint256 _amount) override public onlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredNode(_nodeAddress) {
                            // Must be node's RPL withdrawal address if set or the node's address or an allow listed address or rocketMerkleDistributorMainnet
                            if (msg.sender != getAddress(keccak256(abi.encodePacked("contract.address", "rocketMerkleDistributorMainnet")))) {
                                RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
                                bool fromNode = false;
                                if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
                                    address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
                                    fromNode = msg.sender == rplWithdrawalAddress;
                                } else {
                                    address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                                    fromNode = (msg.sender == _nodeAddress) || (msg.sender == withdrawalAddress);
                                }
                                if (!fromNode) {
                                    require(getBool(keccak256(abi.encodePacked("node.stake.for.allowed", _nodeAddress, msg.sender))), "Not allowed to stake for");
                                }
                            }
                            _stakeRPL(_nodeAddress, _amount);
                        }
                        /// @notice Sets the allow state for this node to perform functions that require locking RPL
                        /// @param _nodeAddress The address of the node operator to change the state for
                        /// @param _allowed Whether locking is allowed or not
                        function setRPLLockingAllowed(address _nodeAddress, bool _allowed) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyRPLWithdrawalAddressOrNode(_nodeAddress) {
                            // Set the value
                            setBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress)), _allowed);
                            // Log it
                            emit RPLLockingAllowed(_nodeAddress, _allowed, block.timestamp);
                        }
                        /// @notice Explicitly allow or remove allowance of an address to be able to stake on behalf of a node
                        /// @dev The node operator is determined by the address calling this method, it is here for backwards compatibility
                        /// @param _caller The address you wish to allow
                        /// @param _allowed Whether the address is allowed or denied
                        function setStakeRPLForAllowed(address _caller, bool _allowed) override external {
                            setStakeRPLForAllowed(msg.sender, _caller, _allowed);
                        }
                        /// @notice Explicitly allow or remove allowance of an address to be able to stake on behalf of a node
                        /// @param _nodeAddress The address of the node operator allowing the caller
                        /// @param _caller The address you wish to allow
                        /// @param _allowed Whether the address is allowed or denied
                        function setStakeRPLForAllowed(address _nodeAddress, address _caller, bool _allowed) override public onlyLatestContract("rocketNodeStaking", address(this)) onlyRPLWithdrawalAddressOrNode(_nodeAddress) {
                            // Set the value
                            setBool(keccak256(abi.encodePacked("node.stake.for.allowed", _nodeAddress, _caller)), _allowed);
                            // Log it
                            emit StakeRPLForAllowed(_nodeAddress, _caller, _allowed, block.timestamp);
                        }
                        /// @dev Internal logic for staking RPL
                        /// @param _nodeAddress The address to increase the RPL stake of
                        /// @param _amount The amount of RPL to stake
                        function _stakeRPL(address _nodeAddress, uint256 _amount) internal {
                            // Load contracts
                            address rplTokenAddress = getContractAddress("rocketTokenRPL");
                            address rocketVaultAddress = getContractAddress("rocketVault");
                            IERC20 rplToken = IERC20(rplTokenAddress);
                            RocketVaultInterface rocketVault = RocketVaultInterface(rocketVaultAddress);
                            // Transfer RPL tokens
                            require(rplToken.transferFrom(msg.sender, address(this), _amount), "Could not transfer RPL to staking contract");
                            // Deposit RPL tokens to vault
                            require(rplToken.approve(rocketVaultAddress, _amount), "Could not approve vault RPL deposit");
                            rocketVault.depositToken("rocketNodeStaking", rplToken, _amount);
                            // Update RPL stake amounts & node RPL staked block
                            increaseTotalRPLStake(_amount);
                            increaseNodeRPLStake(_nodeAddress, _amount);
                            setNodeRPLStakedTime(_nodeAddress, block.timestamp);
                            // Emit RPL staked event
                            emit RPLStaked(_nodeAddress, _amount, block.timestamp);
                        }
                        /// @notice Returns the amount of RPL that is locked for a given node
                        /// @param _nodeAddress The address of the node operator to query for
                        function getNodeRPLLocked(address _nodeAddress) override public view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress)));
                        }
                        /// @notice Locks an amount of RPL from being withdrawn even if the node operator is over capitalised
                        /// @param _nodeAddress The address of the node operator
                        /// @param _amount The amount of RPL to lock
                        function lockRPL(address _nodeAddress, uint256 _amount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() {
                            // Check status
                            require(getBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress))), "Node is not allowed to lock RPL");
                            // The node must have unlocked stake equaling or greater than the amount
                            uint256 rplStake = getNodeRPLStake(_nodeAddress);
                            bytes32 lockedStakeKey = keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress));
                            uint256 lockedStake = getUint(lockedStakeKey);
                            require(rplStake - lockedStake >= _amount, "Not enough staked RPL");
                            // Increase locked RPL
                            setUint(lockedStakeKey, lockedStake + _amount);
                            // Emit event
                            emit RPLLocked(_nodeAddress, _amount, block.timestamp);
                        }
                        /// @notice Unlocks an amount of RPL making it possible to withdraw if the nod is over capitalised
                        /// @param _nodeAddress The address of the node operator
                        /// @param _amount The amount of RPL to unlock
                        function unlockRPL(address _nodeAddress, uint256 _amount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() {
                            // The node must have locked stake equaling or greater than the amount
                            bytes32 lockedStakeKey = keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress));
                            uint256 lockedStake = getUint(lockedStakeKey);
                            require(_amount <= lockedStake, "Not enough locked RPL");
                            // Decrease locked RPL
                            setUint(lockedStakeKey, lockedStake - _amount);
                            // Emit event
                            emit RPLUnlocked(_nodeAddress, _amount, block.timestamp);
                        }
                        /// @notice Transfers RPL from one node to another
                        /// @param _from The node to transfer from
                        /// @param _to The node to transfer to
                        /// @param _amount The amount of RPL to transfer
                        function transferRPL(address _from, address _to, uint256 _amount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() onlyRegisteredNode(_from) {
                            // Check sender has enough RPL
                            require(getNodeRPLStake(_from) >= _amount, "Sender has insufficient RPL");
                            // Transfer the stake
                            decreaseNodeRPLStake(_from, _amount);
                            increaseNodeRPLStake(_to, _amount);
                            // Emit event
                            emit RPLTransferred(_from, _to, _amount, block.timestamp);
                        }
                        /// @notice Withdraw staked RPL back to the node account or withdraw RPL address
                        ///         Can only be called by a node if they have not set their RPL withdrawal address
                        /// @param _amount The amount of RPL to withdraw
                        function withdrawRPL(uint256 _amount) override external {
                            withdrawRPL(msg.sender, _amount);
                        }
                        /// @notice Withdraw staked RPL back to the node account or withdraw RPL address
                        ///         If RPL withdrawal address has been set, must be called from it. Otherwise, must be called from
                        ///         node's primary withdrawal address or their node address.
                        /// @param _nodeAddress The address of the node withdrawing
                        /// @param _amount The amount of RPL to withdraw
                        function withdrawRPL(address _nodeAddress, uint256 _amount) override public onlyLatestContract("rocketNodeStaking", address(this)) {
                            // Check valid node
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            // Check address is permitted to withdraw
                            RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
                            address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
                            if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
                                // If RPL withdrawal address is set, must be called from it
                                require(msg.sender == rplWithdrawalAddress, "Invalid caller");
                            } else {
                                // Otherwise, must be called from node address or withdrawal address
                                address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                                require(msg.sender == _nodeAddress || msg.sender == withdrawalAddress, "Invalid caller");
                            }
                            // Load contracts
                            RocketDAOProtocolSettingsRewardsInterface rocketDAOProtocolSettingsRewards = RocketDAOProtocolSettingsRewardsInterface(getContractAddress("rocketDAOProtocolSettingsRewards"));
                            RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
                            // Check cooldown period (one claim period) has passed since RPL last staked
                            require(block.timestamp - getNodeRPLStakedTime(_nodeAddress) >= rocketDAOProtocolSettingsRewards.getRewardsClaimIntervalTime(), "The withdrawal cooldown period has not passed");
                            // Get & check node's current RPL stake
                            uint256 rplStake = getNodeRPLStake(_nodeAddress);
                            uint256 lockedStake = getNodeRPLLocked(_nodeAddress);
                            require(rplStake - lockedStake >= _amount, "Withdrawal amount exceeds node's staked RPL balance");
                            // Check withdrawal would not under collateralise node
                            require(rplStake - _amount >= getNodeMaximumRPLStake(_nodeAddress) + lockedStake, "Node's staked RPL balance after withdrawal is less than required balance");
                            // Update RPL stake amounts
                            decreaseTotalRPLStake(_amount);
                            decreaseNodeRPLStake(_nodeAddress, _amount);
                            // Transfer RPL tokens to node's RPL withdrawal address (if unset, defaults to primary withdrawal address)
                            rocketVault.withdrawToken(rplWithdrawalAddress, IERC20(getContractAddress("rocketTokenRPL")), _amount);
                            // Emit RPL withdrawn event
                            emit RPLWithdrawn(_nodeAddress, _amount, block.timestamp);
                        }
                        /// @notice Slash a node's RPL by an ETH amount
                        ///         Only accepts calls from registered minipools
                        /// @param _nodeAddress The address to slash RPL from
                        /// @param _ethSlashAmount The amount of RPL to slash denominated in ETH value
                        function slashRPL(address _nodeAddress, uint256 _ethSlashAmount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredMinipool(msg.sender) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
                            // Calculate RPL amount to slash
                            uint256 rplSlashAmount = calcBase * _ethSlashAmount / rocketNetworkPrices.getRPLPrice();
                            // Cap slashed amount to node's RPL stake
                            uint256 rplStake = getNodeRPLStake(_nodeAddress);
                            if (rplSlashAmount > rplStake) { rplSlashAmount = rplStake; }
                            // Transfer slashed amount to auction contract
                            if(rplSlashAmount > 0) rocketVault.transferToken("rocketAuctionManager", IERC20(getContractAddress("rocketTokenRPL")), rplSlashAmount);
                            // Update RPL stake amounts
                            decreaseTotalRPLStake(rplSlashAmount);
                            decreaseNodeRPLStake(_nodeAddress, rplSlashAmount);
                            // Mark minipool as slashed
                            setBool(keccak256(abi.encodePacked("minipool.rpl.slashed", msg.sender)), true);
                            // Emit RPL slashed event
                            emit RPLSlashed(_nodeAddress, rplSlashAmount, _ethSlashAmount, block.timestamp);
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity 0.8.18;
                    pragma abicoder v2;
                    import "../RocketBase.sol";
                    import "../../types/MinipoolStatus.sol";
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolDetails.sol";
                    import "../../interface/dao/node/RocketDAONodeTrustedInterface.sol";
                    import "../../interface/minipool/RocketMinipoolInterface.sol";
                    import "../../interface/minipool/RocketMinipoolManagerInterface.sol";
                    import "../../interface/node/RocketNodeStakingInterface.sol";
                    import "../../interface/util/AddressSetStorageInterface.sol";
                    import "../../interface/node/RocketNodeManagerInterface.sol";
                    import "../../interface/network/RocketNetworkPricesInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
                    import "../../interface/minipool/RocketMinipoolFactoryInterface.sol";
                    import "../../interface/node/RocketNodeDistributorFactoryInterface.sol";
                    import "../../interface/node/RocketNodeDistributorInterface.sol";
                    import "../../interface/network/RocketNetworkPenaltiesInterface.sol";
                    import "../../interface/minipool/RocketMinipoolPenaltyInterface.sol";
                    import "../../interface/node/RocketNodeDepositInterface.sol";
                    import "../network/RocketNetworkSnapshots.sol";
                    import "../node/RocketNodeStaking.sol";
                    /// @notice Minipool creation, removal and management
                    contract RocketMinipoolManager is RocketBase, RocketMinipoolManagerInterface {
                        // Events
                        event MinipoolCreated(address indexed minipool, address indexed node, uint256 time);
                        event MinipoolDestroyed(address indexed minipool, address indexed node, uint256 time);
                        event BeginBondReduction(address indexed minipool, uint256 time);
                        event CancelReductionVoted(address indexed minipool, address indexed member, uint256 time);
                        event ReductionCancelled(address indexed minipool, uint256 time);
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            version = 4;
                        }
                        /// @notice Get the number of minipools in the network
                        function getMinipoolCount() override public view returns (uint256) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getCount(keccak256(bytes("minipools.index")));
                        }
                        /// @notice Get the number of minipools in the network in the Staking state
                        function getStakingMinipoolCount() override public view returns (uint256) {
                            return getUint(keccak256(bytes("minipools.staking.count")));
                        }
                        /// @notice Get the number of finalised minipools in the network
                        function getFinalisedMinipoolCount() override external view returns (uint256) {
                            return getUint(keccak256(bytes("minipools.finalised.count")));
                        }
                        /// @notice Get the number of active minipools in the network
                        function getActiveMinipoolCount() override public view returns (uint256) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            uint256 total = addressSetStorage.getCount(keccak256(bytes("minipools.index")));
                            uint256 finalised = getUint(keccak256(bytes("minipools.finalised.count")));
                            return total - finalised;
                        }
                        /// @notice Returns true if a minipool has had an RPL slashing
                        function getMinipoolRPLSlashed(address _minipoolAddress) override external view returns (bool) {
                            return getBool(keccak256(abi.encodePacked("minipool.rpl.slashed", _minipoolAddress)));
                        }
                        /// @notice Get the number of minipools in each status.
                        ///         Returns the counts for Initialised, Prelaunch, Staking, Withdrawable, and Dissolved in that order.
                        /// @param _offset The offset into the minipool set to start
                        /// @param _limit The maximum number of minipools to iterate
                        function getMinipoolCountPerStatus(uint256 _offset, uint256 _limit) override external view
                        returns (uint256 initialisedCount, uint256 prelaunchCount, uint256 stakingCount, uint256 withdrawableCount, uint256 dissolvedCount) {
                            // Get contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Precompute minipool key
                            bytes32 minipoolKey = keccak256(abi.encodePacked("minipools.index"));
                            // Iterate over the requested minipool range
                            uint256 totalMinipools = getMinipoolCount();
                            uint256 max = _offset + _limit;
                            if (max > totalMinipools || _limit == 0) { max = totalMinipools; }
                            for (uint256 i = _offset; i < max; ++i) {
                                // Get the minipool at index i
                                RocketMinipoolInterface minipool = RocketMinipoolInterface(addressSetStorage.getItem(minipoolKey, i));
                                // Get the minipool's status, and update the appropriate counter
                                MinipoolStatus status = minipool.getStatus();
                                if (status == MinipoolStatus.Initialised) {
                                    initialisedCount++;
                                }
                                else if (status == MinipoolStatus.Prelaunch) {
                                    prelaunchCount++;
                                }
                                else if (status == MinipoolStatus.Staking) {
                                    stakingCount++;
                                }
                                else if (status == MinipoolStatus.Withdrawable) {
                                    withdrawableCount++;
                                }
                                else if (status == MinipoolStatus.Dissolved) {
                                    dissolvedCount++;
                                }
                            }
                        }
                        /// @notice Returns an array of all minipools in the prelaunch state
                        /// @param _offset The offset into the minipool set to start iterating
                        /// @param _limit The maximum number of minipools to iterate over
                        function getPrelaunchMinipools(uint256 _offset, uint256 _limit) override external view
                        returns (address[] memory) {
                            // Get contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Precompute minipool key
                            bytes32 minipoolKey = keccak256(abi.encodePacked("minipools.index"));
                            // Iterate over the requested minipool range
                            uint256 totalMinipools = getMinipoolCount();
                            uint256 max = _offset + _limit;
                            if (max > totalMinipools || _limit == 0) { max = totalMinipools; }
                            // Create array big enough for every minipool
                            address[] memory minipools = new address[](max - _offset);
                            uint256 total = 0;
                            for (uint256 i = _offset; i < max; ++i) {
                                // Get the minipool at index i
                                RocketMinipoolInterface minipool = RocketMinipoolInterface(addressSetStorage.getItem(minipoolKey, i));
                                // Get the minipool's status, and to array if it's in prelaunch
                                MinipoolStatus status = minipool.getStatus();
                                if (status == MinipoolStatus.Prelaunch) {
                                    minipools[total] = address(minipool);
                                    total++;
                                }
                            }
                            // Dirty hack to cut unused elements off end of return value
                            assembly {
                                mstore(minipools, total)
                            }
                            return minipools;
                        }
                        /// @notice Get a network minipool address by index
                        /// @param _index Index into the minipool set to return
                        function getMinipoolAt(uint256 _index) override external view returns (address) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getItem(keccak256(abi.encodePacked("minipools.index")), _index);
                        }
                        /// @notice Get the number of minipools owned by a node
                        /// @param _nodeAddress The node operator to query the count of minipools of
                        function getNodeMinipoolCount(address _nodeAddress) override external view returns (uint256) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getCount(keccak256(abi.encodePacked("node.minipools.index", _nodeAddress)));
                        }
                        /// @notice Get the number of minipools owned by a node that are not finalised
                        /// @param _nodeAddress The node operator to query the count of active minipools of
                        function getNodeActiveMinipoolCount(address _nodeAddress) override public view returns (uint256) {
                            bytes32 key = keccak256(abi.encodePacked("minipools.active.count", _nodeAddress));
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            (bool exists,, uint224 count) = rocketNetworkSnapshots.latest(key);
                            if (!exists){
                                // Fallback to old value
                                AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                                uint256 finalised = getUint(keccak256(abi.encodePacked("node.minipools.finalised.count", _nodeAddress)));
                                uint256 total = addressSetStorage.getCount(keccak256(abi.encodePacked("node.minipools.index", _nodeAddress)));
                                return total - finalised;
                            }
                            return uint256(count);
                        }
                        /// @notice Get the number of minipools owned by a node that are finalised
                        /// @param _nodeAddress The node operator to query the count of finalised minipools of
                        function getNodeFinalisedMinipoolCount(address _nodeAddress) override external view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("node.minipools.finalised.count", _nodeAddress)));
                        }
                        /// @notice Get the number of minipools owned by a node that are in staking status
                        /// @param _nodeAddress The node operator to query the count of staking minipools of
                        function getNodeStakingMinipoolCount(address _nodeAddress) override public view returns (uint256) {
                            RocketNodeDepositInterface rocketNodeDeposit = RocketNodeDepositInterface(getContractAddress("rocketNodeDeposit"));
                            // Get valid deposit amounts
                            uint256[] memory depositSizes = rocketNodeDeposit.getDepositAmounts();
                            uint256 total;
                            for (uint256 i = 0; i < depositSizes.length; ++i){
                                total = total + getNodeStakingMinipoolCountBySize(_nodeAddress, depositSizes[i]);
                            }
                            return total;
                        }
                        /// @notice Get the number of minipools owned by a node that are in staking status
                        /// @param _nodeAddress The node operator to query the count of minipools by desposit size of
                        /// @param _depositSize The deposit size to filter result by
                        function getNodeStakingMinipoolCountBySize(address _nodeAddress, uint256 _depositSize) override public view returns (uint256) {
                            bytes32 nodeKey;
                            if (_depositSize == 16 ether){
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", _nodeAddress));
                            } else {
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", _nodeAddress, _depositSize));
                            }
                            return getUint(nodeKey);
                        }
                        /// @notice Get a node minipool address by index
                        /// @param _nodeAddress The node operator to query the minipool of
                        /// @param _index Index into the node operator's set of minipools
                        function getNodeMinipoolAt(address _nodeAddress, uint256 _index) override external view returns (address) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getItem(keccak256(abi.encodePacked("node.minipools.index", _nodeAddress)), _index);
                        }
                        /// @notice Get the number of validating minipools owned by a node
                        /// @param _nodeAddress The node operator to query the count of validating minipools of
                        function getNodeValidatingMinipoolCount(address _nodeAddress) override external view returns (uint256) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getCount(keccak256(abi.encodePacked("node.minipools.validating.index", _nodeAddress)));
                        }
                        /// @notice Get a validating node minipool address by index
                        /// @param _nodeAddress The node operator to query the validating minipool of
                        /// @param _index Index into the node operator's set of validating minipools
                        function getNodeValidatingMinipoolAt(address _nodeAddress, uint256 _index) override external view returns (address) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getItem(keccak256(abi.encodePacked("node.minipools.validating.index", _nodeAddress)), _index);
                        }
                        /// @notice Get a minipool address by validator pubkey
                        /// @param _pubkey The pubkey to query
                        function getMinipoolByPubkey(bytes memory _pubkey) override public view returns (address) {
                            return getAddress(keccak256(abi.encodePacked("validator.minipool", _pubkey)));
                        }
                        /// @notice Returns true if a minipool exists
                        /// @param _minipoolAddress The address of the minipool to check the existence of
                        function getMinipoolExists(address _minipoolAddress) override public view returns (bool) {
                            return getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress)));
                        }
                        /// @notice Returns true if a minipool previously existed at the given address
                        /// @param _minipoolAddress The address to check the previous existence of a minipool at
                        function getMinipoolDestroyed(address _minipoolAddress) override external view returns (bool) {
                            return getBool(keccak256(abi.encodePacked("minipool.destroyed", _minipoolAddress)));
                        }
                        /// @notice Returns a minipool's validator pubkey
                        /// @param _minipoolAddress The minipool to query the pubkey of
                        function getMinipoolPubkey(address _minipoolAddress) override public view returns (bytes memory) {
                            return getBytes(keccak256(abi.encodePacked("minipool.pubkey", _minipoolAddress)));
                        }
                        /// @notice Calculates what the withdrawal credentials of a minipool should be set to
                        /// @param _minipoolAddress The minipool to calculate the withdrawal credentials for
                        function getMinipoolWithdrawalCredentials(address _minipoolAddress) override public pure returns (bytes memory) {
                            return abi.encodePacked(bytes1(0x01), bytes11(0x0), address(_minipoolAddress));
                        }
                        /// @notice Decrements a node operator's number of staking minipools based on the minipools prior bond amount and
                        ///         increments it based on their new bond amount.
                        /// @param _previousBond The minipool's previous bond value
                        /// @param _newBond The minipool's new bond value
                        /// @param _previousFee The fee of the minipool prior to the bond change
                        /// @param _newFee The fee of the minipool after the bond change
                        function updateNodeStakingMinipoolCount(uint256 _previousBond, uint256 _newBond, uint256 _previousFee, uint256 _newFee) override external onlyLatestContract("rocketMinipoolManager", address(this)) onlyRegisteredMinipool(msg.sender) {
                            bytes32 nodeKey;
                            bytes32 numeratorKey;
                            // Get contracts
                            RocketMinipoolInterface minipool = RocketMinipoolInterface(msg.sender);
                            address nodeAddress = minipool.getNodeAddress();
                            // Try to distribute current fees at previous average commission rate
                            _tryDistribute(nodeAddress);
                            // Decrement previous bond count
                            if (_previousBond == 16 ether){
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", nodeAddress));
                                numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", nodeAddress));
                            } else {
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", nodeAddress, _previousBond));
                                numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", nodeAddress, _previousBond));
                            }
                            subUint(nodeKey, 1);
                            subUint(numeratorKey, _previousFee);
                            // Increment new bond count
                            if (_newBond == 16 ether){
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", nodeAddress));
                                numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", nodeAddress));
                            } else {
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", nodeAddress, _newBond));
                                numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", nodeAddress, _newBond));
                            }
                            addUint(nodeKey, 1);
                            addUint(numeratorKey, _newFee);
                        }
                        /// @dev Increments a node operator's number of staking minipools and calculates updated average node fee.
                        ///      Must be called from the minipool itself as msg.sender is used to query the minipool's node fee
                        /// @param _nodeAddress The node address to increment the number of staking minipools of
                        function incrementNodeStakingMinipoolCount(address _nodeAddress) override external onlyLatestContract("rocketMinipoolManager", address(this)) onlyRegisteredMinipool(msg.sender) {
                            // Get contracts
                            RocketMinipoolInterface minipool = RocketMinipoolInterface(msg.sender);
                            // Try to distribute current fees at previous average commission rate
                            _tryDistribute(_nodeAddress);
                            // Update the node specific count
                            uint256 depositSize = minipool.getNodeDepositBalance();
                            bytes32 nodeKey;
                            bytes32 numeratorKey;
                            if (depositSize == 16 ether){
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", _nodeAddress));
                                numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", _nodeAddress));
                            } else {
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", _nodeAddress, depositSize));
                                numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", _nodeAddress, depositSize));
                            }
                            uint256 nodeValue = getUint(nodeKey);
                            setUint(nodeKey, nodeValue + 1);
                            // Update the total count
                            bytes32 totalKey = keccak256(abi.encodePacked("minipools.staking.count"));
                            uint256 totalValue = getUint(totalKey);
                            setUint(totalKey, totalValue + 1);
                            // Update node fee average
                            addUint(numeratorKey, minipool.getNodeFee());
                        }
                        /// @dev Decrements a node operator's number of minipools in staking status and calculates updated average node fee.
                        ///      Must be called from the minipool itself as msg.sender is used to query the minipool's node fee
                        /// @param _nodeAddress The node address to decrement the number of staking minipools of
                        function decrementNodeStakingMinipoolCount(address _nodeAddress) override external onlyLatestContract("rocketMinipoolManager", address(this)) onlyRegisteredMinipool(msg.sender) {
                            // Get contracts
                            RocketMinipoolInterface minipool = RocketMinipoolInterface(msg.sender);
                            // Try to distribute current fees at previous average commission rate
                            _tryDistribute(_nodeAddress);
                            // Update the node specific count
                            uint256 depositSize = minipool.getNodeDepositBalance();
                            bytes32 nodeKey;
                            bytes32 numeratorKey;
                            if (depositSize == 16 ether){
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", _nodeAddress));
                                numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", _nodeAddress));
                            } else {
                                nodeKey = keccak256(abi.encodePacked("node.minipools.staking.count", _nodeAddress, depositSize));
                                numeratorKey = keccak256(abi.encodePacked("node.average.fee.numerator", _nodeAddress, depositSize));
                            }
                            uint256 nodeValue = getUint(nodeKey);
                            setUint(nodeKey, nodeValue - 1);
                            // Update the total count
                            bytes32 totalKey = keccak256(abi.encodePacked("minipools.staking.count"));
                            uint256 totalValue = getUint(totalKey);
                            setUint(totalKey, totalValue - 1);
                            // Update node fee average
                            subUint(numeratorKey, minipool.getNodeFee());
                        }
                        /// @notice Calls distribute on the given node's distributor if it has a balance and has been initialised
                        /// @dev Reverts if node has not initialised their distributor
                        /// @param _nodeAddress The node operator to try distribute rewards for
                        function tryDistribute(address _nodeAddress) override external {
                            _tryDistribute(_nodeAddress);
                        }
                        /// @dev Calls distribute on the given node's distributor if it has a balance and has been initialised
                        /// @param _nodeAddress The node operator to try distribute rewards for
                        function _tryDistribute(address _nodeAddress) internal {
                            // Get contracts
                            RocketNodeDistributorFactoryInterface rocketNodeDistributorFactory = RocketNodeDistributorFactoryInterface(getContractAddress("rocketNodeDistributorFactory"));
                            address distributorAddress = rocketNodeDistributorFactory.getProxyAddress(_nodeAddress);
                            // If there are funds to distribute than call distribute
                            if (distributorAddress.balance > 0) {
                                // Get contracts
                                RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
                                // Ensure distributor has been initialised
                                require(rocketNodeManager.getFeeDistributorInitialised(_nodeAddress), "Distributor not initialised");
                                RocketNodeDistributorInterface distributor = RocketNodeDistributorInterface(distributorAddress);
                                distributor.distribute();
                            }
                        }
                        /// @dev Increments a node operator's number of minipools that have been finalised
                        /// @param _nodeAddress The node operator to increment finalised minipool count for
                        function incrementNodeFinalisedMinipoolCount(address _nodeAddress) override external onlyLatestContract("rocketMinipoolManager", address(this)) onlyRegisteredMinipool(msg.sender) {
                            // Get active minipool count (before increasing finalised count in case of fallback calculation)
                            uint256 activeMinipoolCount = getNodeActiveMinipoolCount(_nodeAddress);
                            // Can only finalise a minipool once
                            bytes32 finalisedKey = keccak256(abi.encodePacked("node.minipools.finalised", msg.sender));
                            require(!getBool(finalisedKey), "Minipool has already been finalised");
                            setBool(finalisedKey, true);
                            // Update the node specific count
                            addUint(keccak256(abi.encodePacked("node.minipools.finalised.count", _nodeAddress)), 1);
                            // Update the total count
                            addUint(keccak256(bytes("minipools.finalised.count")), 1);
                            // Update ETH matched
                            RocketNodeStakingInterface rocketNodeStaking = RocketNodeStakingInterface(getContractAddress("rocketNodeStaking"));
                            RocketNetworkSnapshots rocketNetworkSnapshots = RocketNetworkSnapshots(getContractAddress("rocketNetworkSnapshots"));
                            uint256 ethMatched = rocketNodeStaking.getNodeETHMatched(_nodeAddress);
                            ethMatched -= RocketMinipoolInterface(msg.sender).getUserDepositBalance();
                            bytes32 key = keccak256(abi.encodePacked("eth.matched.node.amount", _nodeAddress));
                            rocketNetworkSnapshots.push(key, uint224(ethMatched));
                            // Decrement active count
                            key = keccak256(abi.encodePacked("minipools.active.count", _nodeAddress));
                            rocketNetworkSnapshots.push(key, uint224(activeMinipoolCount - 1));
                        }
                        /// @dev Create a minipool. Only accepts calls from the RocketNodeDeposit contract
                        /// @param _nodeAddress The owning node operator's address
                        /// @param _salt A salt used in determining the minipool's address
                        function createMinipool(address _nodeAddress, uint256 _salt) override public onlyLatestContract("rocketMinipoolManager", address(this)) onlyLatestContract("rocketNodeDeposit", msg.sender) returns (RocketMinipoolInterface) {
                            // Load contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Check node minipool limit based on RPL stake
                            { // Local scope to prevent stack too deep error
                              RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
                              // Check global minipool limit
                              uint256 totalActiveMinipoolCount = getActiveMinipoolCount();
                              require(totalActiveMinipoolCount + 1 <= rocketDAOProtocolSettingsMinipool.getMaximumCount(), "Global minipool limit reached");
                            }
                            // Get current active minipool count for this node operator (before we insert into address set in case it uses fallback calc)
                            uint256 activeMinipoolCount = getNodeActiveMinipoolCount(_nodeAddress);
                            // Create minipool contract
                            address contractAddress = deployContract(_nodeAddress, _salt);
                            // Initialise minipool data
                            setBool(keccak256(abi.encodePacked("minipool.exists", contractAddress)), true);
                            // Add minipool to indexes
                            addressSetStorage.addItem(keccak256(abi.encodePacked("minipools.index")), contractAddress);
                            addressSetStorage.addItem(keccak256(abi.encodePacked("node.minipools.index", _nodeAddress)), contractAddress);
                            // Increment active count
                            RocketNetworkSnapshots rocketNetworkSnapshots = RocketNetworkSnapshots(getContractAddress("rocketNetworkSnapshots"));
                            bytes32 key = keccak256(abi.encodePacked("minipools.active.count", _nodeAddress));
                            rocketNetworkSnapshots.push(key, uint224(activeMinipoolCount + 1));
                            // Emit minipool created event
                            emit MinipoolCreated(contractAddress, _nodeAddress, block.timestamp);
                            // Return created minipool address
                            return RocketMinipoolInterface(contractAddress);
                        }
                        /// @notice Creates a vacant minipool that can be promoted by changing the given validator's withdrawal credentials
                        /// @param _nodeAddress Address of the owning node operator
                        /// @param _salt A salt used in determining the minipool's address
                        /// @param _validatorPubkey A validator pubkey that the node operator intends to migrate the withdrawal credentials of
                        /// @param _bondAmount The bond amount selected by the node operator
                        /// @param _currentBalance The current balance of the validator on the beaconchain (will be checked by oDAO and scrubbed if not correct)
                        function createVacantMinipool(address _nodeAddress, uint256 _salt, bytes calldata _validatorPubkey, uint256 _bondAmount, uint256 _currentBalance) override external onlyLatestContract("rocketMinipoolManager", address(this)) onlyLatestContract("rocketNodeDeposit", msg.sender) returns (RocketMinipoolInterface) {
                            // Get contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Create the minipool
                            RocketMinipoolInterface minipool = createMinipool(_nodeAddress, _salt);
                            // Prepare the minipool
                            minipool.prepareVacancy(_bondAmount, _currentBalance);
                            // Set the minipool's validator pubkey
                            _setMinipoolPubkey(address(minipool), _validatorPubkey);
                            // Add minipool to the vacant set
                            addressSetStorage.addItem(keccak256(abi.encodePacked("minipools.vacant.index")), address(minipool));
                            // Return
                            return minipool;
                        }
                        /// @dev Called by minipool to remove from vacant set on promotion or dissolution
                        function removeVacantMinipool() override external onlyLatestContract("rocketMinipoolManager", address(this)) onlyRegisteredMinipool(msg.sender) {
                            // Remove from vacant set
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            addressSetStorage.removeItem(keccak256(abi.encodePacked("minipools.vacant.index")), msg.sender);
                            // If minipool was dissolved, remove mapping of pubkey to minipool to allow NO to try again in future
                            RocketMinipoolInterface minipool = RocketMinipoolInterface(msg.sender);
                            if (minipool.getStatus() == MinipoolStatus.Dissolved) {
                                bytes memory pubkey = getMinipoolPubkey(msg.sender);
                                deleteAddress(keccak256(abi.encodePacked("validator.minipool", pubkey)));
                            }
                        }
                        /// @notice Returns the number of minipools in the vacant minipool set
                        function getVacantMinipoolCount() override external view returns (uint256) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getCount(keccak256(abi.encodePacked("minipools.vacant.index")));
                        }
                        /// @notice Returns the vacant minipool at a given index
                        /// @param _index The index into the vacant minipool set to retrieve
                        function getVacantMinipoolAt(uint256 _index) override external view returns (address) {
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            return addressSetStorage.getItem(keccak256(abi.encodePacked("minipools.vacant.index")), _index);
                        }
                        /// @dev Destroy a minipool cleaning up all relevant state. Only accepts calls from registered minipools
                        function destroyMinipool() override external onlyLatestContract("rocketMinipoolManager", address(this)) onlyRegisteredMinipool(msg.sender) {
                            // Load contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Initialize minipool & get properties
                            RocketMinipoolInterface minipool = RocketMinipoolInterface(msg.sender);
                            address nodeAddress = minipool.getNodeAddress();
                            // Update ETH matched
                            RocketNodeStakingInterface rocketNodeStaking = RocketNodeStakingInterface(getContractAddress("rocketNodeStaking"));
                            uint256 ethMatched = rocketNodeStaking.getNodeETHMatched(nodeAddress);
                            ethMatched = ethMatched - minipool.getUserDepositBalance();
                            // Record in snapshot manager
                            RocketNetworkSnapshots rocketNetworkSnapshots = RocketNetworkSnapshots(getContractAddress("rocketNetworkSnapshots"));
                            bytes32 key = keccak256(abi.encodePacked("eth.matched.node.amount", nodeAddress));
                            rocketNetworkSnapshots.push(key, uint224(ethMatched));
                            // Update minipool data
                            setBool(keccak256(abi.encodePacked("minipool.exists", msg.sender)), false);
                            // Record minipool as destroyed to prevent recreation at same address
                            setBool(keccak256(abi.encodePacked("minipool.destroyed", msg.sender)), true);
                            // Get number of active minipools (before removing from address set in case of fallback calculation)
                            uint256 activeMinipoolCount = getNodeActiveMinipoolCount(nodeAddress);
                            // Remove minipool from indexes
                            addressSetStorage.removeItem(keccak256(abi.encodePacked("minipools.index")), msg.sender);
                            addressSetStorage.removeItem(keccak256(abi.encodePacked("node.minipools.index", nodeAddress)), msg.sender);
                            // Clean up pubkey state
                            bytes memory pubkey = getMinipoolPubkey(msg.sender);
                            deleteBytes(keccak256(abi.encodePacked("minipool.pubkey", msg.sender)));
                            deleteAddress(keccak256(abi.encodePacked("validator.minipool", pubkey)));
                            // Decrement active count
                            key = keccak256(abi.encodePacked("minipools.active.count", nodeAddress));
                            rocketNetworkSnapshots.push(key, uint224(activeMinipoolCount - 1));
                            // Emit minipool destroyed event
                            emit MinipoolDestroyed(msg.sender, nodeAddress, block.timestamp);
                        }
                        /// @dev Set a minipool's validator pubkey. Only accepts calls from registered minipools
                        /// @param _pubkey The pubkey to set for the calling minipool
                        function setMinipoolPubkey(bytes calldata _pubkey) override public onlyLatestContract("rocketMinipoolManager", address(this)) onlyRegisteredMinipool(msg.sender) {
                            _setMinipoolPubkey(msg.sender, _pubkey);
                        }
                        /// @dev Internal logic to set a minipool's pubkey, reverts if pubkey already set
                        /// @param _pubkey The pubkey to set for the calling minipool
                        function _setMinipoolPubkey(address _minipool, bytes calldata _pubkey) private {
                            // Check validator pubkey is not in use
                            require(getMinipoolByPubkey(_pubkey) == address(0x0), "Validator pubkey is in use");
                            // Load contracts
                            AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
                            // Initialise minipool & get properties
                            RocketMinipoolInterface minipool = RocketMinipoolInterface(_minipool);
                            address nodeAddress = minipool.getNodeAddress();
                            // Set minipool validator pubkey & validator minipool address
                            setBytes(keccak256(abi.encodePacked("minipool.pubkey", _minipool)), _pubkey);
                            setAddress(keccak256(abi.encodePacked("validator.minipool", _pubkey)), _minipool);
                            // Add minipool to node validating minipools index
                            addressSetStorage.addItem(keccak256(abi.encodePacked("node.minipools.validating.index", nodeAddress)), _minipool);
                        }
                        /// @dev Wrapper around minipool getDepositType which handles backwards compatibility with v1 and v2 delegates
                        /// @param _minipoolAddress Minipool address to get the deposit type of
                        function getMinipoolDepositType(address _minipoolAddress) external override view returns (MinipoolDeposit) {
                            RocketMinipoolInterface minipoolInterface = RocketMinipoolInterface(_minipoolAddress);
                            uint8 version = 1;
                            // Version 1 minipools did not have a version() function
                            try minipoolInterface.version() returns (uint8 tryVersion) {
                                version = tryVersion;
                            } catch (bytes memory /*lowLevelData*/) {}
                            if (version == 1 || version == 2) {
                                try minipoolInterface.getDepositType{gas: 30000}() returns (MinipoolDeposit depositType) {
                                    return depositType;
                                } catch (bytes memory /*lowLevelData*/) {
                                    return MinipoolDeposit.Variable;
                                }
                            }
                            return minipoolInterface.getDepositType();
                        }
                        /// @dev Performs a CREATE2 deployment of a minipool contract with given salt
                        /// @param _nodeAddress The owning node operator's address
                        /// @param _salt A salt used in determining the minipool's address
                        function deployContract(address _nodeAddress, uint256 _salt) private returns (address) {
                            RocketMinipoolFactoryInterface rocketMinipoolFactory = RocketMinipoolFactoryInterface(getContractAddress("rocketMinipoolFactory"));
                            return rocketMinipoolFactory.deployContract(_nodeAddress, _salt);
                        }
                    }
                    

                    File 9 of 10: RocketNodeStaking
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                    pragma solidity >0.5.0 <0.9.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);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    /// @title Base settings / modifiers for each contract in Rocket Pool
                    /// @author David Rugendyke
                    abstract contract RocketBase {
                        // Calculate using this as the base
                        uint256 constant calcBase = 1 ether;
                        // Version of the contract
                        uint8 public version;
                        // The main storage contract where primary persistant storage is maintained
                        RocketStorageInterface rocketStorage = RocketStorageInterface(address(0));
                        /*** Modifiers **********************************************************/
                        /**
                        * @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
                        */
                        modifier onlyLatestNetworkContract() {
                            require(getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))), "Invalid or outdated network contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that doesn't match one of the supplied contract or is the latest version of that contract
                        */
                        modifier onlyLatestContract(string memory _contractName, address _contractAddress) {
                            require(_contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", _contractName))), "Invalid or outdated contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered node
                        */
                        modifier onlyRegisteredNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a trusted node DAO member
                        */
                        modifier onlyTrustedNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("dao.trustednodes.", "member", _nodeAddress))), "Invalid trusted node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered minipool
                        */
                        modifier onlyRegisteredMinipool(address _minipoolAddress) {
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress))), "Invalid minipool");
                            _;
                        }
                        
                        /**
                        * @dev Throws if called by any account other than a guardian account (temporary account allowed access to settings before DAO is fully enabled)
                        */
                        modifier onlyGuardian() {
                            require(msg.sender == rocketStorage.getGuardian(), "Account is not a temporary guardian");
                            _;
                        }
                        /*** Methods **********************************************************/
                        /// @dev Set the main Rocket Storage address
                        constructor(RocketStorageInterface _rocketStorageAddress) {
                            // Update the contract address
                            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
                        }
                        /// @dev Get the address of a network contract by name
                        function getContractAddress(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Check it
                            require(contractAddress != address(0x0), "Contract not found");
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)
                        function getContractAddressUnsafe(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the name of a network contract by address
                        function getContractName(address _contractAddress) internal view returns (string memory) {
                            // Get the contract name
                            string memory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
                            // Check it
                            require(bytes(contractName).length > 0, "Contract not found");
                            // Return
                            return contractName;
                        }
                        /// @dev Get revert error message from a .call method
                        function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return "Transaction reverted silently";
                            assembly {
                                // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string)); // All that remains is the revert string
                        }
                        /*** Rocket Storage Methods ****************************************/
                        // Note: Unused helpers have been removed to keep contract sizes down
                        /// @dev Storage get methods
                        function getAddress(bytes32 _key) internal view returns (address) { return rocketStorage.getAddress(_key); }
                        function getUint(bytes32 _key) internal view returns (uint) { return rocketStorage.getUint(_key); }
                        function getString(bytes32 _key) internal view returns (string memory) { return rocketStorage.getString(_key); }
                        function getBytes(bytes32 _key) internal view returns (bytes memory) { return rocketStorage.getBytes(_key); }
                        function getBool(bytes32 _key) internal view returns (bool) { return rocketStorage.getBool(_key); }
                        function getInt(bytes32 _key) internal view returns (int) { return rocketStorage.getInt(_key); }
                        function getBytes32(bytes32 _key) internal view returns (bytes32) { return rocketStorage.getBytes32(_key); }
                        /// @dev Storage set methods
                        function setAddress(bytes32 _key, address _value) internal { rocketStorage.setAddress(_key, _value); }
                        function setUint(bytes32 _key, uint _value) internal { rocketStorage.setUint(_key, _value); }
                        function setString(bytes32 _key, string memory _value) internal { rocketStorage.setString(_key, _value); }
                        function setBytes(bytes32 _key, bytes memory _value) internal { rocketStorage.setBytes(_key, _value); }
                        function setBool(bytes32 _key, bool _value) internal { rocketStorage.setBool(_key, _value); }
                        function setInt(bytes32 _key, int _value) internal { rocketStorage.setInt(_key, _value); }
                        function setBytes32(bytes32 _key, bytes32 _value) internal { rocketStorage.setBytes32(_key, _value); }
                        /// @dev Storage delete methods
                        function deleteAddress(bytes32 _key) internal { rocketStorage.deleteAddress(_key); }
                        function deleteUint(bytes32 _key) internal { rocketStorage.deleteUint(_key); }
                        function deleteString(bytes32 _key) internal { rocketStorage.deleteString(_key); }
                        function deleteBytes(bytes32 _key) internal { rocketStorage.deleteBytes(_key); }
                        function deleteBool(bytes32 _key) internal { rocketStorage.deleteBool(_key); }
                        function deleteInt(bytes32 _key) internal { rocketStorage.deleteInt(_key); }
                        function deleteBytes32(bytes32 _key) internal { rocketStorage.deleteBytes32(_key); }
                        /// @dev Storage arithmetic methods
                        function addUint(bytes32 _key, uint256 _amount) internal { rocketStorage.addUint(_key, _amount); }
                        function subUint(bytes32 _key, uint256 _amount) internal { rocketStorage.subUint(_key, _amount); }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents the type of deposits required by a minipool
                    enum MinipoolDeposit {
                        None,       // Marks an invalid deposit type
                        Full,       // The minipool requires 32 ETH from the node operator, 16 ETH of which will be refinanced from user deposits
                        Half,       // The minipool required 16 ETH from the node operator to be matched with 16 ETH from user deposits
                        Empty,      // The minipool requires 0 ETH from the node operator to be matched with 32 ETH from user deposits (trusted nodes only)
                        Variable    // Indicates this minipool is of the new generation that supports a variable deposit amount
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // Represents a minipool's status within the network
                    enum MinipoolStatus {
                        Initialised,    // The minipool has been initialised and is awaiting a deposit of user ETH
                        Prelaunch,      // The minipool has enough ETH to begin staking and is awaiting launch by the node operator
                        Staking,        // The minipool is currently staking
                        Withdrawable,   // NO LONGER USED
                        Dissolved       // The minipool has been dissolved and its user deposited ETH has been returned to the deposit pool
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "./MinipoolDeposit.sol";
                    import "./MinipoolStatus.sol";
                    // A struct containing all the information on-chain about a specific minipool
                    struct MinipoolDetails {
                        bool exists;
                        address minipoolAddress;
                        bytes pubkey;
                        MinipoolStatus status;
                        uint256 statusBlock;
                        uint256 statusTime;
                        bool finalised;
                        MinipoolDeposit depositType;
                        uint256 nodeFee;
                        uint256 nodeDepositBalance;
                        bool nodeDepositAssigned;
                        uint256 userDepositBalance;
                        bool userDepositAssigned;
                        uint256 userDepositAssignedTime;
                        bool useLatestDelegate;
                        address delegate;
                        address previousDelegate;
                        address effectiveDelegate;
                        uint256 penaltyCount;
                        uint256 penaltyRate;
                        address nodeAddress;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolStatus.sol";
                    import "../RocketStorageInterface.sol";
                    interface RocketMinipoolInterface {
                        function version() external view returns (uint8);
                        function initialise(address _nodeAddress) external;
                        function getStatus() external view returns (MinipoolStatus);
                        function getFinalised() external view returns (bool);
                        function getStatusBlock() external view returns (uint256);
                        function getStatusTime() external view returns (uint256);
                        function getScrubVoted(address _member) external view returns (bool);
                        function getDepositType() external view returns (MinipoolDeposit);
                        function getNodeAddress() external view returns (address);
                        function getNodeFee() external view returns (uint256);
                        function getNodeDepositBalance() external view returns (uint256);
                        function getNodeRefundBalance() external view returns (uint256);
                        function getNodeDepositAssigned() external view returns (bool);
                        function getPreLaunchValue() external view returns (uint256);
                        function getNodeTopUpValue() external view returns (uint256);
                        function getVacant() external view returns (bool);
                        function getPreMigrationBalance() external view returns (uint256);
                        function getUserDistributed() external view returns (bool);
                        function getUserDepositBalance() external view returns (uint256);
                        function getUserDepositAssigned() external view returns (bool);
                        function getUserDepositAssignedTime() external view returns (uint256);
                        function getTotalScrubVotes() external view returns (uint256);
                        function calculateNodeShare(uint256 _balance) external view returns (uint256);
                        function calculateUserShare(uint256 _balance) external view returns (uint256);
                        function preDeposit(uint256 _bondingValue, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot) external payable;
                        function deposit() external payable;
                        function userDeposit() external payable;
                        function distributeBalance(bool _rewardsOnly) external;
                        function beginUserDistribute() external;
                        function userDistributeAllowed() external view returns (bool);
                        function refund() external;
                        function slash() external;
                        function finalise() external;
                        function canStake() external view returns (bool);
                        function canPromote() external view returns (bool);
                        function stake(bytes calldata _validatorSignature, bytes32 _depositDataRoot) external;
                        function prepareVacancy(uint256 _bondAmount, uint256 _currentBalance) external;
                        function promote() external;
                        function dissolve() external;
                        function close() external;
                        function voteScrub() external;
                        function reduceBondAmount() external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../types/MinipoolDeposit.sol";
                    import "../../types/MinipoolDetails.sol";
                    import "./RocketMinipoolInterface.sol";
                    interface RocketMinipoolManagerInterface {
                        function getMinipoolCount() external view returns (uint256);
                        function getStakingMinipoolCount() external view returns (uint256);
                        function getFinalisedMinipoolCount() external view returns (uint256);
                        function getActiveMinipoolCount() external view returns (uint256);
                        function getMinipoolRPLSlashed(address _minipoolAddress) external view returns (bool);
                        function getMinipoolCountPerStatus(uint256 offset, uint256 limit) external view returns (uint256, uint256, uint256, uint256, uint256);
                        function getPrelaunchMinipools(uint256 offset, uint256 limit) external view returns (address[] memory);
                        function getMinipoolAt(uint256 _index) external view returns (address);
                        function getNodeMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeActiveMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeFinalisedMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeStakingMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeStakingMinipoolCountBySize(address _nodeAddress, uint256 _depositSize) external view returns (uint256);
                        function getNodeMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address);
                        function getNodeValidatingMinipoolCount(address _nodeAddress) external view returns (uint256);
                        function getNodeValidatingMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address);
                        function getMinipoolByPubkey(bytes calldata _pubkey) external view returns (address);
                        function getMinipoolExists(address _minipoolAddress) external view returns (bool);
                        function getMinipoolDestroyed(address _minipoolAddress) external view returns (bool);
                        function getMinipoolPubkey(address _minipoolAddress) external view returns (bytes memory);
                        function updateNodeStakingMinipoolCount(uint256 _previousBond, uint256 _newBond, uint256 _previousFee, uint256 _newFee) external;
                        function getMinipoolWithdrawalCredentials(address _minipoolAddress) external pure returns (bytes memory);
                        function createMinipool(address _nodeAddress, uint256 _salt) external returns (RocketMinipoolInterface);
                        function createVacantMinipool(address _nodeAddress, uint256 _salt, bytes calldata _validatorPubkey, uint256 _bondAmount, uint256 _currentBalance) external returns (RocketMinipoolInterface);
                        function removeVacantMinipool() external;
                        function getVacantMinipoolCount() external view returns (uint256);
                        function getVacantMinipoolAt(uint256 _index) external view returns (address);
                        function destroyMinipool() external;
                        function incrementNodeStakingMinipoolCount(address _nodeAddress) external;
                        function decrementNodeStakingMinipoolCount(address _nodeAddress) external;
                        function tryDistribute(address _nodeAddress) external;
                        function incrementNodeFinalisedMinipoolCount(address _nodeAddress) external;
                        function setMinipoolPubkey(bytes calldata _pubkey) external;
                        function getMinipoolDepositType(address _minipoolAddress) external view returns (MinipoolDeposit);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketNetworkPricesInterface {
                        function getPricesBlock() external view returns (uint256);
                        function getRPLPrice() external view returns (uint256);
                        function submitPrices(uint256 _block, uint256 _slotTimestamp, uint256 _rplPrice) external;
                        function executeUpdatePrices(uint256 _block, uint256 _slotTimestamp, uint256 _rplPrice) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    interface RocketNodeStakingInterface {
                        function getTotalRPLStake() external view returns (uint256);
                        function getNodeRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatched(address _nodeAddress) external view returns (uint256);
                        function getNodeETHProvided(address _nodeAddress) external view returns (uint256);
                        function getNodeETHCollateralisationRatio(address _nodeAddress) external view returns (uint256);
                        function getNodeRPLStakedTime(address _nodeAddress) external view returns (uint256);
                        function getNodeEffectiveRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMinimumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeMaximumRPLStake(address _nodeAddress) external view returns (uint256);
                        function getNodeETHMatchedLimit(address _nodeAddress) external view returns (uint256);
                        function getRPLLockingAllowed(address _nodeAddress) external view returns (bool);
                        function stakeRPL(uint256 _amount) external;
                        function stakeRPLFor(address _nodeAddress, uint256 _amount) external;
                        function setRPLLockingAllowed(address _nodeAddress, bool _allowed) external;
                        function setStakeRPLForAllowed(address _caller, bool _allowed) external;
                        function setStakeRPLForAllowed(address _nodeAddress, address _caller, bool _allowed) external;
                        function getNodeRPLLocked(address _nodeAddress) external view returns (uint256);
                        function lockRPL(address _nodeAddress, uint256 _amount) external;
                        function unlockRPL(address _nodeAddress, uint256 _amount) external;
                        function transferRPL(address _from, address _to, uint256 _amount) external;
                        function withdrawRPL(uint256 _amount) external;
                        function withdrawRPL(address _nodeAddress, uint256 _amount) external;
                        function slashRPL(address _nodeAddress, uint256 _ethSlashAmount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    interface RocketDAOProtocolSettingsRewardsInterface {
                        function setSettingRewardsClaimers(uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent) external;
                        function getRewardsClaimerPerc(string memory _contractName) external view returns (uint256);
                        function getRewardsClaimersPerc() external view returns (uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent);
                        function getRewardsClaimersTrustedNodePerc() external view returns (uint256);
                        function getRewardsClaimersProtocolPerc() external view returns (uint256);
                        function getRewardsClaimersNodePerc() external view returns (uint256);
                        function getRewardsClaimersTimeUpdated() external view returns (uint256);
                        function getRewardsClaimIntervalPeriods() external view returns (uint256);
                        function getRewardsClaimIntervalTime() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../../../../types/MinipoolDeposit.sol";
                    interface RocketDAOProtocolSettingsMinipoolInterface {
                        function getLaunchBalance() external view returns (uint256);
                        function getPreLaunchValue() external pure returns (uint256);
                        function getDepositUserAmount(MinipoolDeposit _depositType) external view returns (uint256);
                        function getFullDepositUserAmount() external view returns (uint256);
                        function getHalfDepositUserAmount() external view returns (uint256);
                        function getVariableDepositAmount() external view returns (uint256);
                        function getSubmitWithdrawableEnabled() external view returns (bool);
                        function getBondReductionEnabled() external view returns (bool);
                        function getLaunchTimeout() external view returns (uint256);
                        function getMaximumCount() external view returns (uint256);
                        function isWithinUserDistributeWindow(uint256 _time) external view returns (bool);
                        function hasUserDistributeWindowPassed(uint256 _time) external view returns (bool);
                        function getUserDistributeWindowStart() external view returns (uint256);
                        function getUserDistributeWindowLength() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketDAOProtocolSettingsNodeInterface {
                        function getRegistrationEnabled() external view returns (bool);
                        function getSmoothingPoolRegistrationEnabled() external view returns (bool);
                        function getDepositEnabled() external view returns (bool);
                        function getVacantMinipoolsEnabled() external view returns (bool);
                        function getMinimumPerMinipoolStake() external view returns (uint256);
                        function getMaximumPerMinipoolStake() external view returns (uint256);
                        function getMaximumStakeForVotingPower() external view returns (uint256);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                    import "./IERC20.sol";
                    pragma solidity >0.5.0 <0.9.0;
                    interface IERC20Burnable is IERC20 {
                        function burn(uint256 amount) external;
                        function burnFrom(address account, uint256 amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "./util/IERC20Burnable.sol";
                    interface RocketVaultInterface {
                        function balanceOf(string memory _networkContractName) external view returns (uint256);
                        function depositEther() external payable;
                        function withdrawEther(uint256 _amount) external;
                        function depositToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external;
                        function withdrawToken(address _withdrawalAddress, IERC20 _tokenAddress, uint256 _amount) external;
                        function balanceOfToken(string memory _networkContractName, IERC20 _tokenAddress) external view returns (uint256);
                        function transferToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external;
                        function burnToken(IERC20Burnable _tokenAddress, uint256 _amount) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface AddressSetStorageInterface {
                        function getCount(bytes32 _key) external view returns (uint);
                        function getItem(bytes32 _key, uint _index) external view returns (address);
                        function getIndexOf(bytes32 _key, address _value) external view returns (int);
                        function addItem(bytes32 _key, address _value) external;
                        function removeItem(bytes32 _key, address _value) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    struct Checkpoint224 {
                        uint32 _block;
                        uint224 _value;
                    }
                    /// @notice Accounting for snapshotting of values based on block numbers
                    interface RocketNetworkSnapshotsInterface {
                        function push(bytes32 _key, uint224 _value) external;
                        function length(bytes32 _key) external view returns (uint256);
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224);
                        function latestBlock(bytes32 _key) external view returns (uint32);
                        function latestValue(bytes32 _key) external view returns (uint224);
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224);
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224);
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev Standard math utilities missing in the Solidity language.
                     */
                    library Math {
                        enum Rounding {
                            Down, // Toward negative infinity
                            Up, // Toward infinity
                            Zero // Toward zero
                        }
                        /**
                         * @dev Returns the largest of two numbers.
                         */
                        function max(uint256 a, uint256 b) internal pure returns (uint256) {
                            return a > b ? a : b;
                        }
                        /**
                         * @dev Returns the smallest of two numbers.
                         */
                        function min(uint256 a, uint256 b) internal pure returns (uint256) {
                            return a < b ? a : b;
                        }
                        /**
                         * @dev Returns the average of two numbers. The result is rounded towards
                         * zero.
                         */
                        function average(uint256 a, uint256 b) internal pure returns (uint256) {
                            // (a + b) / 2 can overflow.
                            return (a & b) + (a ^ b) / 2;
                        }
                        /**
                         * @dev Returns the ceiling of the division of two numbers.
                         *
                         * This differs from standard division with `/` in that it rounds up instead
                         * of rounding down.
                         */
                        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                            // (a + b - 1) / b can overflow on addition, so we distribute.
                            return a == 0 ? 0 : (a - 1) / b + 1;
                        }
                        /**
                         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                         * with further edits by Uniswap Labs also under MIT license.
                         */
                        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                            unchecked {
                                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                // variables such that product = prod1 * 2^256 + prod0.
                                uint256 prod0; // Least significant 256 bits of the product
                                uint256 prod1; // Most significant 256 bits of the product
                                assembly {
                                    let mm := mulmod(x, y, not(0))
                                    prod0 := mul(x, y)
                                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                }
                                // Handle non-overflow cases, 256 by 256 division.
                                if (prod1 == 0) {
                                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                                    // The surrounding unchecked block does not change this fact.
                                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                                    return prod0 / denominator;
                                }
                                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                require(denominator > prod1, "Math: mulDiv overflow");
                                ///////////////////////////////////////////////
                                // 512 by 256 division.
                                ///////////////////////////////////////////////
                                // Make division exact by subtracting the remainder from [prod1 prod0].
                                uint256 remainder;
                                assembly {
                                    // Compute remainder using mulmod.
                                    remainder := mulmod(x, y, denominator)
                                    // Subtract 256 bit number from 512 bit number.
                                    prod1 := sub(prod1, gt(remainder, prod0))
                                    prod0 := sub(prod0, remainder)
                                }
                                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                // See https://cs.stackexchange.com/q/138556/92363.
                                // Does not overflow because the denominator cannot be zero at this stage in the function.
                                uint256 twos = denominator & (~denominator + 1);
                                assembly {
                                    // Divide denominator by twos.
                                    denominator := div(denominator, twos)
                                    // Divide [prod1 prod0] by twos.
                                    prod0 := div(prod0, twos)
                                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                                    twos := add(div(sub(0, twos), twos), 1)
                                }
                                // Shift in bits from prod1 into prod0.
                                prod0 |= prod1 * twos;
                                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                // four bits. That is, denominator * inv = 1 mod 2^4.
                                uint256 inverse = (3 * denominator) ^ 2;
                                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                // in modular arithmetic, doubling the correct bits in each step.
                                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                // is no longer required.
                                result = prod0 * inverse;
                                return result;
                            }
                        }
                        /**
                         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                         */
                        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                            uint256 result = mulDiv(x, y, denominator);
                            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                                result += 1;
                            }
                            return result;
                        }
                        /**
                         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                         *
                         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                         */
                        function sqrt(uint256 a) internal pure returns (uint256) {
                            if (a == 0) {
                                return 0;
                            }
                            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                            //
                            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                            //
                            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                            //
                            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                            uint256 result = 1 << (log2(a) >> 1);
                            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                            // into the expected uint128 result.
                            unchecked {
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                return min(result, a / result);
                            }
                        }
                        /**
                         * @notice Calculates sqrt(a), following the selected rounding direction.
                         */
                        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = sqrt(a);
                                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 2, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log2(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >> 128 > 0) {
                                    value >>= 128;
                                    result += 128;
                                }
                                if (value >> 64 > 0) {
                                    value >>= 64;
                                    result += 64;
                                }
                                if (value >> 32 > 0) {
                                    value >>= 32;
                                    result += 32;
                                }
                                if (value >> 16 > 0) {
                                    value >>= 16;
                                    result += 16;
                                }
                                if (value >> 8 > 0) {
                                    value >>= 8;
                                    result += 8;
                                }
                                if (value >> 4 > 0) {
                                    value >>= 4;
                                    result += 4;
                                }
                                if (value >> 2 > 0) {
                                    value >>= 2;
                                    result += 2;
                                }
                                if (value >> 1 > 0) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log2(value);
                                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 10, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log10(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >= 10 ** 64) {
                                    value /= 10 ** 64;
                                    result += 64;
                                }
                                if (value >= 10 ** 32) {
                                    value /= 10 ** 32;
                                    result += 32;
                                }
                                if (value >= 10 ** 16) {
                                    value /= 10 ** 16;
                                    result += 16;
                                }
                                if (value >= 10 ** 8) {
                                    value /= 10 ** 8;
                                    result += 8;
                                }
                                if (value >= 10 ** 4) {
                                    value /= 10 ** 4;
                                    result += 4;
                                }
                                if (value >= 10 ** 2) {
                                    value /= 10 ** 2;
                                    result += 2;
                                }
                                if (value >= 10 ** 1) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log10(value);
                                return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 256, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         *
                         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                         */
                        function log256(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >> 128 > 0) {
                                    value >>= 128;
                                    result += 16;
                                }
                                if (value >> 64 > 0) {
                                    value >>= 64;
                                    result += 8;
                                }
                                if (value >> 32 > 0) {
                                    value >>= 32;
                                    result += 4;
                                }
                                if (value >> 16 > 0) {
                                    value >>= 16;
                                    result += 2;
                                }
                                if (value >> 8 > 0) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log256(value);
                                return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                            }
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // Copyright (c) 2016-2023 zOS Global Limited and contributors
                    // Adapted from OpenZeppelin `Checkpoints` contract
                    pragma solidity 0.8.18;
                    import "@openzeppelin4/contracts/utils/math/Math.sol";
                    import "../RocketBase.sol";
                    import "../../interface/network/RocketNetworkSnapshotsInterface.sol";
                    /// @notice Accounting for snapshotting of values based on block numbers
                    contract RocketNetworkSnapshots is RocketBase, RocketNetworkSnapshotsInterface {
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            // Set contract version
                            version = 1;
                        }
                        function push(bytes32 _key, uint224 _value) onlyLatestContract("rocketNetworkSnapshots", address(this)) onlyLatestNetworkContract external {
                            _insert(_key, _value);
                        }
                        function length(bytes32 _key) public view returns (uint256) {
                            return rocketStorage.getUint(keccak256(abi.encodePacked("snapshot.length", _key)));
                        }
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224) {
                            uint256 len = length(_key);
                            if (len == 0) {
                                return (false, 0, 0);
                            }
                            Checkpoint224 memory checkpoint = _load(_key, len - 1);
                            return (true, checkpoint._block, checkpoint._value);
                        }
                        function latestBlock(bytes32 _key) external view returns (uint32) {
                            uint256 len = length(_key);
                            return len == 0 ? 0 : _blockAt(_key, len - 1);
                        }
                        function latestValue(bytes32 _key) external view returns (uint224) {
                            uint256 len = length(_key);
                            return len == 0 ? 0 : _valueAt(_key, len - 1);
                        }
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224) {
                            uint256 len = length(_key);
                            uint256 pos = _binaryLookup(_key, _block, 0, len);
                            return pos == 0 ? 0 : _valueAt(_key, pos - 1);
                        }
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224) {
                            uint256 len = length(_key);
                            uint256 low = 0;
                            uint256 high = len;
                            if (len > 5 && len > _recency) {
                                uint256 mid = len - _recency;
                                if (_block < _blockAt(_key, mid)) {
                                    high = mid;
                                } else {
                                    low = mid + 1;
                                }
                            }
                            uint256 pos = _binaryLookup(_key, _block, low, high);
                            return pos == 0 ? 0 : _valueAt(_key, pos - 1);
                        }
                        function _insert(bytes32 _key, uint224 _value) private {
                            uint32 blockNumber = uint32(block.number);
                            uint256 pos = length(_key);
                            if (pos > 0) {
                                Checkpoint224 memory last = _load(_key, pos - 1);
                                // Checkpoint keys must be non-decreasing.
                                require (last._block <= blockNumber, "Unordered snapshot insertion");
                                // Update or push new checkpoint
                                if (last._block == blockNumber) {
                                    last._value = _value;
                                    _set(_key, pos - 1, last);
                                } else {
                                    _push(_key, Checkpoint224({_block: blockNumber, _value: _value}));
                                }
                            } else {
                                _push(_key, Checkpoint224({_block: blockNumber, _value: _value}));
                            }
                        }
                        function _binaryLookup(
                            bytes32 _key,
                            uint32 _block,
                            uint256 _low,
                            uint256 _high
                        ) private view returns (uint256) {
                            while (_low < _high) {
                                uint256 mid = Math.average(_low, _high);
                                if (_blockAt(_key, mid) > _block) {
                                    _high = mid;
                                } else {
                                    _low = mid + 1;
                                }
                            }
                            return _high;
                        }
                        function _load(bytes32 _key, uint256 _pos) private view returns (Checkpoint224 memory) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            Checkpoint224 memory result;
                            result._block = uint32(uint256(raw) >> 224);
                            result._value = uint224(uint256(raw));
                            return result;
                        }
                        function _blockAt(bytes32 _key, uint256 _pos) private view returns (uint32) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            return uint32(uint256(raw) >> 224);
                        }
                        function _valueAt(bytes32 _key, uint256 _pos) private view returns (uint224) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            return uint224(uint256(raw));
                        }
                        function _push(bytes32 _key, Checkpoint224 memory _item) private {
                            bytes32 lengthKey = keccak256(abi.encodePacked("snapshot.length", _key));
                            uint256 snapshotLength = rocketStorage.getUint(lengthKey);
                            bytes32 key = bytes32(uint256(_key) + snapshotLength);
                            rocketStorage.setUint(lengthKey, snapshotLength + 1);
                            rocketStorage.setBytes32(key, _encode(_item));
                        }
                        function _set(bytes32 _key, uint256 _pos, Checkpoint224 memory _item) private {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            rocketStorage.setBytes32(key, _encode(_item));
                        }
                        function _encode(Checkpoint224 memory _item) private pure returns (bytes32) {
                            return bytes32(
                                uint256(_item._block) << 224 | uint256(_item._value)
                            );
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    // A struct containing all the information on-chain about a specific node
                    struct NodeDetails {
                        bool exists;
                        uint256 registrationTime;
                        string timezoneLocation;
                        bool feeDistributorInitialised;
                        address feeDistributorAddress;
                        uint256 rewardNetwork;
                        uint256 rplStake;
                        uint256 effectiveRPLStake;
                        uint256 minimumRPLStake;
                        uint256 maximumRPLStake;
                        uint256 ethMatched;
                        uint256 ethMatchedLimit;
                        uint256 minipoolCount;
                        uint256 balanceETH;
                        uint256 balanceRETH;
                        uint256 balanceRPL;
                        uint256 balanceOldRPL;
                        uint256 depositCreditBalance;
                        uint256 distributorBalanceUserETH;
                        uint256 distributorBalanceNodeETH;
                        address withdrawalAddress;
                        address pendingWithdrawalAddress;
                        bool smoothingPoolRegistrationState;
                        uint256 smoothingPoolRegistrationChanged;
                        address nodeAddress;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    pragma abicoder v2;
                    import "../../types/NodeDetails.sol";
                    interface RocketNodeManagerInterface {
                        // Structs
                        struct TimezoneCount {
                            string timezone;
                            uint256 count;
                        }
                        function getNodeCount() external view returns (uint256);
                        function getNodeCountPerTimezone(uint256 offset, uint256 limit) external view returns (TimezoneCount[] memory);
                        function getNodeAt(uint256 _index) external view returns (address);
                        function getNodeExists(address _nodeAddress) external view returns (bool);
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeRPLWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeRPLWithdrawalAddressIsSet(address _nodeAddress) external view returns (bool);
                        function unsetRPLWithdrawalAddress(address _nodeAddress) external;
                        function setRPLWithdrawalAddress(address _nodeAddress, address _newRPLWithdrawalAddress, bool _confirm) external;
                        function confirmRPLWithdrawalAddress(address _nodeAddress) external;
                        function getNodePendingRPLWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodeTimezoneLocation(address _nodeAddress) external view returns (string memory);
                        function registerNode(string calldata _timezoneLocation) external;
                        function getNodeRegistrationTime(address _nodeAddress) external view returns (uint256);
                        function setTimezoneLocation(string calldata _timezoneLocation) external;
                        function setRewardNetwork(address _nodeAddress, uint256 network) external;
                        function getRewardNetwork(address _nodeAddress) external view returns (uint256);
                        function getFeeDistributorInitialised(address _nodeAddress) external view returns (bool);
                        function initialiseFeeDistributor() external;
                        function getAverageNodeFee(address _nodeAddress) external view returns (uint256);
                        function setSmoothingPoolRegistrationState(bool _state) external;
                        function getSmoothingPoolRegistrationState(address _nodeAddress) external returns (bool);
                        function getSmoothingPoolRegistrationChanged(address _nodeAddress) external returns (uint256);
                        function getSmoothingPoolRegisteredNodeCount(uint256 _offset, uint256 _limit) external view returns (uint256);
                        function getNodeDetails(address _nodeAddress) external view returns (NodeDetails memory);
                        function getNodeAddresses(uint256 _offset, uint256 _limit) external view returns (address[] memory);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity 0.8.18;
                    import "../../interface/util/IERC20.sol";
                    import "../RocketBase.sol";
                    import "../../interface/minipool/RocketMinipoolManagerInterface.sol";
                    import "../../interface/network/RocketNetworkPricesInterface.sol";
                    import "../../interface/node/RocketNodeStakingInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsRewardsInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
                    import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
                    import "../../interface/RocketVaultInterface.sol";
                    import "../../interface/util/AddressSetStorageInterface.sol";
                    import "../../interface/network/RocketNetworkSnapshotsInterface.sol";
                    import "../network/RocketNetworkSnapshots.sol";
                    import "../../interface/node/RocketNodeManagerInterface.sol";
                    /// @notice Handles node deposits and minipool creation
                    contract RocketNodeStaking is RocketBase, RocketNodeStakingInterface {
                        // Constants
                        bytes32 immutable internal totalKey;
                        // Events
                        event RPLStaked(address indexed from, uint256 amount, uint256 time);
                        event RPLWithdrawn(address indexed to, uint256 amount, uint256 time);
                        event RPLSlashed(address indexed node, uint256 amount, uint256 ethValue, uint256 time);
                        event StakeRPLForAllowed(address indexed node, address indexed caller, bool allowed, uint256 time);
                        event RPLLockingAllowed(address indexed node, bool allowed, uint256 time);
                        event RPLLocked(address indexed from, uint256 amount, uint256 time);
                        event RPLUnlocked(address indexed from, uint256 amount, uint256 time);
                        event RPLTransferred(address indexed from, address indexed to, uint256 amount, uint256 time);
                        modifier onlyRPLWithdrawalAddressOrNode(address _nodeAddress) {
                            // Check that the call is coming from RPL withdrawal address (or node if unset)
                            RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
                            if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
                                address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
                                require(msg.sender == rplWithdrawalAddress, "Must be called from RPL withdrawal address");
                            } else {
                                require(msg.sender == _nodeAddress, "Must be called from node address");
                            }
                            _;
                        }
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            version = 5;
                            // Precompute keys
                            totalKey = keccak256(abi.encodePacked("rpl.staked.total.amount"));
                        }
                        /// @notice Returns the total quantity of RPL staked on the network
                        function getTotalRPLStake() override external view returns (uint256) {
                            return getUint(totalKey);
                        }
                        /// @dev Increases the total network RPL stake
                        /// @param _amount How much to increase by
                        function increaseTotalRPLStake(uint256 _amount) private {
                            addUint(totalKey, _amount);
                        }
                        /// @dev Decrease the total network RPL stake
                        /// @param _amount How much to decrease by
                        function decreaseTotalRPLStake(uint256 _amount) private {
                            subUint(totalKey, _amount);
                        }
                        /// @notice Returns the amount a given node operator has staked
                        /// @param _nodeAddress The address of the node operator to query
                        function getNodeRPLStake(address _nodeAddress) override public view returns (uint256) {
                            bytes32 key = keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            (bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
                            uint256 stake = uint256(value);
                            if (!exists){
                                // Fallback to old value
                                stake = getUint(key);
                            }
                            return stake;
                        }
                        /// @dev Increases a node operator's RPL stake
                        /// @param _amount How much to increase by
                        function increaseNodeRPLStake(address _nodeAddress, uint256 _amount) private {
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            bytes32 key = keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
                            (bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
                            if (!exists){
                                value = uint224(getUint(key));
                            }
                            rocketNetworkSnapshots.push(key, value + uint224(_amount));
                        }
                        /// @dev Decrease a node operator's RPL stake
                        /// @param _amount How much to decrease by
                        function decreaseNodeRPLStake(address _nodeAddress, uint256 _amount) private {
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            bytes32 key = keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
                            (bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
                            if (!exists){
                                value = uint224(getUint(key));
                            }
                            rocketNetworkSnapshots.push(key, value - uint224(_amount));
                        }
                        /// @notice Returns a node's matched ETH amount (amount taken from protocol to stake)
                        /// @param _nodeAddress The address of the node operator to query
                        function getNodeETHMatched(address _nodeAddress) override public view returns (uint256) {
                            RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
                            bytes32 key = keccak256(abi.encodePacked("eth.matched.node.amount", _nodeAddress));
                            (bool exists, , uint224 value) = rocketNetworkSnapshots.latest(key);
                            if (exists) {
                                // Value was previously set in a snapshot so return that
                                return value;
                            } else {
                                // Fallback to old method
                                uint256 ethMatched = getUint(key);
                                if (ethMatched > 0) {
                                    // Value was previously calculated and stored so return that
                                    return ethMatched;
                                } else {
                                    // Fallback for backwards compatibility before ETH matched was recorded (all legacy minipools matched 16 ETH from protocol)
                                    RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                                    return rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress) * 16 ether;
                                }
                            }
                        }
                        /// @notice Returns a node's provided ETH amount (amount supplied to create minipools)
                        /// @param _nodeAddress The address of the node operator to query
                        function getNodeETHProvided(address _nodeAddress) override public view returns (uint256) {
                            // Get contracts
                            RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                            uint256 activeMinipoolCount = rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress);
                            // Retrieve stored ETH matched value
                            uint256 ethMatched = getNodeETHMatched(_nodeAddress);
                            if (ethMatched > 0) {
                                RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
                                uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
                                // ETH provided is number of staking minipools * 32 - eth matched
                                uint256 totalEthStaked = activeMinipoolCount * launchAmount;
                                return totalEthStaked - ethMatched;
                            } else {
                                // Fallback for legacy minipools is number of staking minipools * 16
                                return activeMinipoolCount * 16 ether;
                            }
                        }
                        /// @notice Returns the ratio between capital taken from users and provided by a node operator.
                        ///         The value is a 1e18 precision fixed point integer value of (node capital + user capital) / node capital.
                        /// @param _nodeAddress The address of the node operator to query
                        function getNodeETHCollateralisationRatio(address _nodeAddress) override public view returns (uint256) {
                            uint256 ethMatched = getNodeETHMatched(_nodeAddress);
                            if (ethMatched == 0) {
                                // Node operator only has legacy minipools and all legacy minipools had a 1:1 ratio
                                return calcBase * 2;
                            } else {
                                RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
                                uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
                                RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
                                uint256 totalEthStaked = rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress) * launchAmount;
                                return (totalEthStaked * calcBase) / (totalEthStaked - ethMatched);
                            }
                        }
                        /// @notice Returns the timestamp at which a node last staked RPL
                        function getNodeRPLStakedTime(address _nodeAddress) override public view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("rpl.staked.node.time", _nodeAddress)));
                        }
                        /// @dev Sets the timestamp at which a node last staked RPL
                        /// @param _nodeAddress The address of the node operator to set the value for
                        /// @param _time The timestamp to set
                        function setNodeRPLStakedTime(address _nodeAddress, uint256 _time) private {
                            setUint(keccak256(abi.encodePacked("rpl.staked.node.time", _nodeAddress)), _time);
                        }
                        /// @notice Calculate and return a node's effective RPL stake amount
                        /// @param _nodeAddress The address of the node operator to calculate for
                        function getNodeEffectiveRPLStake(address _nodeAddress) override public view returns (uint256) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Get node's current RPL stake
                            uint256 rplStake = getNodeRPLStake(_nodeAddress);
                            // Retrieve variables for calculations
                            uint256 matchedETH = getNodeETHMatched(_nodeAddress);
                            uint256 providedETH = getNodeETHProvided(_nodeAddress);
                            uint256 rplPrice = rocketNetworkPrices.getRPLPrice();
                            // RPL stake cannot exceed maximum
                            uint256 maximumStakePercent = rocketDAOProtocolSettingsNode.getMaximumPerMinipoolStake();
                            uint256 maximumStake = providedETH * maximumStakePercent / rplPrice;
                            if (rplStake > maximumStake) {
                                return maximumStake;
                            }
                            // If RPL stake is lower than minimum, node has no effective stake
                            uint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
                            uint256 minimumStake = matchedETH * minimumStakePercent / rplPrice;
                            if (rplStake < minimumStake) {
                                return 0;
                            }
                            // Otherwise, return the actual stake
                            return rplStake;
                        }
                        /// @notice Calculate and return a node's minimum RPL stake to collateralize their minipools
                        /// @param _nodeAddress The address of the node operator to calculate for
                        function getNodeMinimumRPLStake(address _nodeAddress) override external view returns (uint256) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Retrieve variables
                            uint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
                            uint256 matchedETH = getNodeETHMatched(_nodeAddress);
                            return matchedETH * minimumStakePercent / rocketNetworkPrices.getRPLPrice();
                        }
                        /// @notice Calculate and return a node's maximum RPL stake to fully collateralise their minipools
                        /// @param _nodeAddress The address of the node operator to calculate for
                        function getNodeMaximumRPLStake(address _nodeAddress) override public view returns (uint256) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Retrieve variables
                            uint256 maximumStakePercent = rocketDAOProtocolSettingsNode.getMaximumPerMinipoolStake();
                            uint256 providedETH = getNodeETHProvided(_nodeAddress);
                            return providedETH * maximumStakePercent / rocketNetworkPrices.getRPLPrice();
                        }
                        /// @notice Calculate and return a node's limit of how much user ETH they can use based on RPL stake
                        /// @param _nodeAddress The address of the node operator to calculate for
                        function getNodeETHMatchedLimit(address _nodeAddress) override external view returns (uint256) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
                            // Calculate & return limit
                            uint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
                            return getNodeRPLStake(_nodeAddress) *rocketNetworkPrices.getRPLPrice() / minimumStakePercent;
                        }
                        /// @notice Returns whether this node allows RPL locking or not
                        /// @param _nodeAddress The address of the node operator to query for
                        function getRPLLockingAllowed(address _nodeAddress) external view returns (bool) {
                            return getBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress)));
                        }
                        /// @notice Accept an RPL stake from the node operator's own address
                        ///         Requires the node's RPL withdrawal address to be unset
                        /// @param _amount The amount of RPL to stake
                        function stakeRPL(uint256 _amount) override external {
                            stakeRPLFor(msg.sender, _amount);
                        }
                        /// @notice Accept an RPL stake from any address for a specified node
                        ///         Requires caller to have approved this contract to spend RPL
                        ///         Requires caller to be on the node operator's allow list (see `setStakeForAllowed`)
                        /// @param _nodeAddress The address of the node operator to stake on behalf of
                        /// @param _amount The amount of RPL to stake
                        function stakeRPLFor(address _nodeAddress, uint256 _amount) override public onlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredNode(_nodeAddress) {
                            // Must be node's RPL withdrawal address if set or the node's address or an allow listed address or rocketMerkleDistributorMainnet
                            if (msg.sender != getAddress(keccak256(abi.encodePacked("contract.address", "rocketMerkleDistributorMainnet")))) {
                                RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
                                bool fromNode = false;
                                if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
                                    address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
                                    fromNode = msg.sender == rplWithdrawalAddress;
                                } else {
                                    address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                                    fromNode = (msg.sender == _nodeAddress) || (msg.sender == withdrawalAddress);
                                }
                                if (!fromNode) {
                                    require(getBool(keccak256(abi.encodePacked("node.stake.for.allowed", _nodeAddress, msg.sender))), "Not allowed to stake for");
                                }
                            }
                            _stakeRPL(_nodeAddress, _amount);
                        }
                        /// @notice Sets the allow state for this node to perform functions that require locking RPL
                        /// @param _nodeAddress The address of the node operator to change the state for
                        /// @param _allowed Whether locking is allowed or not
                        function setRPLLockingAllowed(address _nodeAddress, bool _allowed) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyRPLWithdrawalAddressOrNode(_nodeAddress) {
                            // Set the value
                            setBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress)), _allowed);
                            // Log it
                            emit RPLLockingAllowed(_nodeAddress, _allowed, block.timestamp);
                        }
                        /// @notice Explicitly allow or remove allowance of an address to be able to stake on behalf of a node
                        /// @dev The node operator is determined by the address calling this method, it is here for backwards compatibility
                        /// @param _caller The address you wish to allow
                        /// @param _allowed Whether the address is allowed or denied
                        function setStakeRPLForAllowed(address _caller, bool _allowed) override external {
                            setStakeRPLForAllowed(msg.sender, _caller, _allowed);
                        }
                        /// @notice Explicitly allow or remove allowance of an address to be able to stake on behalf of a node
                        /// @param _nodeAddress The address of the node operator allowing the caller
                        /// @param _caller The address you wish to allow
                        /// @param _allowed Whether the address is allowed or denied
                        function setStakeRPLForAllowed(address _nodeAddress, address _caller, bool _allowed) override public onlyLatestContract("rocketNodeStaking", address(this)) onlyRPLWithdrawalAddressOrNode(_nodeAddress) {
                            // Set the value
                            setBool(keccak256(abi.encodePacked("node.stake.for.allowed", _nodeAddress, _caller)), _allowed);
                            // Log it
                            emit StakeRPLForAllowed(_nodeAddress, _caller, _allowed, block.timestamp);
                        }
                        /// @dev Internal logic for staking RPL
                        /// @param _nodeAddress The address to increase the RPL stake of
                        /// @param _amount The amount of RPL to stake
                        function _stakeRPL(address _nodeAddress, uint256 _amount) internal {
                            // Load contracts
                            address rplTokenAddress = getContractAddress("rocketTokenRPL");
                            address rocketVaultAddress = getContractAddress("rocketVault");
                            IERC20 rplToken = IERC20(rplTokenAddress);
                            RocketVaultInterface rocketVault = RocketVaultInterface(rocketVaultAddress);
                            // Transfer RPL tokens
                            require(rplToken.transferFrom(msg.sender, address(this), _amount), "Could not transfer RPL to staking contract");
                            // Deposit RPL tokens to vault
                            require(rplToken.approve(rocketVaultAddress, _amount), "Could not approve vault RPL deposit");
                            rocketVault.depositToken("rocketNodeStaking", rplToken, _amount);
                            // Update RPL stake amounts & node RPL staked block
                            increaseTotalRPLStake(_amount);
                            increaseNodeRPLStake(_nodeAddress, _amount);
                            setNodeRPLStakedTime(_nodeAddress, block.timestamp);
                            // Emit RPL staked event
                            emit RPLStaked(_nodeAddress, _amount, block.timestamp);
                        }
                        /// @notice Returns the amount of RPL that is locked for a given node
                        /// @param _nodeAddress The address of the node operator to query for
                        function getNodeRPLLocked(address _nodeAddress) override public view returns (uint256) {
                            return getUint(keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress)));
                        }
                        /// @notice Locks an amount of RPL from being withdrawn even if the node operator is over capitalised
                        /// @param _nodeAddress The address of the node operator
                        /// @param _amount The amount of RPL to lock
                        function lockRPL(address _nodeAddress, uint256 _amount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() {
                            // Check status
                            require(getBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress))), "Node is not allowed to lock RPL");
                            // The node must have unlocked stake equaling or greater than the amount
                            uint256 rplStake = getNodeRPLStake(_nodeAddress);
                            bytes32 lockedStakeKey = keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress));
                            uint256 lockedStake = getUint(lockedStakeKey);
                            require(rplStake - lockedStake >= _amount, "Not enough staked RPL");
                            // Increase locked RPL
                            setUint(lockedStakeKey, lockedStake + _amount);
                            // Emit event
                            emit RPLLocked(_nodeAddress, _amount, block.timestamp);
                        }
                        /// @notice Unlocks an amount of RPL making it possible to withdraw if the nod is over capitalised
                        /// @param _nodeAddress The address of the node operator
                        /// @param _amount The amount of RPL to unlock
                        function unlockRPL(address _nodeAddress, uint256 _amount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() {
                            // The node must have locked stake equaling or greater than the amount
                            bytes32 lockedStakeKey = keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress));
                            uint256 lockedStake = getUint(lockedStakeKey);
                            require(_amount <= lockedStake, "Not enough locked RPL");
                            // Decrease locked RPL
                            setUint(lockedStakeKey, lockedStake - _amount);
                            // Emit event
                            emit RPLUnlocked(_nodeAddress, _amount, block.timestamp);
                        }
                        /// @notice Transfers RPL from one node to another
                        /// @param _from The node to transfer from
                        /// @param _to The node to transfer to
                        /// @param _amount The amount of RPL to transfer
                        function transferRPL(address _from, address _to, uint256 _amount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() onlyRegisteredNode(_from) {
                            // Check sender has enough RPL
                            require(getNodeRPLStake(_from) >= _amount, "Sender has insufficient RPL");
                            // Transfer the stake
                            decreaseNodeRPLStake(_from, _amount);
                            increaseNodeRPLStake(_to, _amount);
                            // Emit event
                            emit RPLTransferred(_from, _to, _amount, block.timestamp);
                        }
                        /// @notice Withdraw staked RPL back to the node account or withdraw RPL address
                        ///         Can only be called by a node if they have not set their RPL withdrawal address
                        /// @param _amount The amount of RPL to withdraw
                        function withdrawRPL(uint256 _amount) override external {
                            withdrawRPL(msg.sender, _amount);
                        }
                        /// @notice Withdraw staked RPL back to the node account or withdraw RPL address
                        ///         If RPL withdrawal address has been set, must be called from it. Otherwise, must be called from
                        ///         node's primary withdrawal address or their node address.
                        /// @param _nodeAddress The address of the node withdrawing
                        /// @param _amount The amount of RPL to withdraw
                        function withdrawRPL(address _nodeAddress, uint256 _amount) override public onlyLatestContract("rocketNodeStaking", address(this)) {
                            // Check valid node
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            // Check address is permitted to withdraw
                            RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
                            address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
                            if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
                                // If RPL withdrawal address is set, must be called from it
                                require(msg.sender == rplWithdrawalAddress, "Invalid caller");
                            } else {
                                // Otherwise, must be called from node address or withdrawal address
                                address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
                                require(msg.sender == _nodeAddress || msg.sender == withdrawalAddress, "Invalid caller");
                            }
                            // Load contracts
                            RocketDAOProtocolSettingsRewardsInterface rocketDAOProtocolSettingsRewards = RocketDAOProtocolSettingsRewardsInterface(getContractAddress("rocketDAOProtocolSettingsRewards"));
                            RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
                            // Check cooldown period (one claim period) has passed since RPL last staked
                            require(block.timestamp - getNodeRPLStakedTime(_nodeAddress) >= rocketDAOProtocolSettingsRewards.getRewardsClaimIntervalTime(), "The withdrawal cooldown period has not passed");
                            // Get & check node's current RPL stake
                            uint256 rplStake = getNodeRPLStake(_nodeAddress);
                            uint256 lockedStake = getNodeRPLLocked(_nodeAddress);
                            require(rplStake - lockedStake >= _amount, "Withdrawal amount exceeds node's staked RPL balance");
                            // Check withdrawal would not under collateralise node
                            require(rplStake - _amount >= getNodeMaximumRPLStake(_nodeAddress) + lockedStake, "Node's staked RPL balance after withdrawal is less than required balance");
                            // Update RPL stake amounts
                            decreaseTotalRPLStake(_amount);
                            decreaseNodeRPLStake(_nodeAddress, _amount);
                            // Transfer RPL tokens to node's RPL withdrawal address (if unset, defaults to primary withdrawal address)
                            rocketVault.withdrawToken(rplWithdrawalAddress, IERC20(getContractAddress("rocketTokenRPL")), _amount);
                            // Emit RPL withdrawn event
                            emit RPLWithdrawn(_nodeAddress, _amount, block.timestamp);
                        }
                        /// @notice Slash a node's RPL by an ETH amount
                        ///         Only accepts calls from registered minipools
                        /// @param _nodeAddress The address to slash RPL from
                        /// @param _ethSlashAmount The amount of RPL to slash denominated in ETH value
                        function slashRPL(address _nodeAddress, uint256 _ethSlashAmount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredMinipool(msg.sender) {
                            // Load contracts
                            RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
                            RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
                            // Calculate RPL amount to slash
                            uint256 rplSlashAmount = calcBase * _ethSlashAmount / rocketNetworkPrices.getRPLPrice();
                            // Cap slashed amount to node's RPL stake
                            uint256 rplStake = getNodeRPLStake(_nodeAddress);
                            if (rplSlashAmount > rplStake) { rplSlashAmount = rplStake; }
                            // Transfer slashed amount to auction contract
                            if(rplSlashAmount > 0) rocketVault.transferToken("rocketAuctionManager", IERC20(getContractAddress("rocketTokenRPL")), rplSlashAmount);
                            // Update RPL stake amounts
                            decreaseTotalRPLStake(rplSlashAmount);
                            decreaseNodeRPLStake(_nodeAddress, rplSlashAmount);
                            // Mark minipool as slashed
                            setBool(keccak256(abi.encodePacked("minipool.rpl.slashed", msg.sender)), true);
                            // Emit RPL slashed event
                            emit RPLSlashed(_nodeAddress, rplSlashAmount, _ethSlashAmount, block.timestamp);
                        }
                    }
                    

                    File 10 of 10: RocketNetworkSnapshots
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev Standard math utilities missing in the Solidity language.
                     */
                    library Math {
                        enum Rounding {
                            Down, // Toward negative infinity
                            Up, // Toward infinity
                            Zero // Toward zero
                        }
                        /**
                         * @dev Returns the largest of two numbers.
                         */
                        function max(uint256 a, uint256 b) internal pure returns (uint256) {
                            return a > b ? a : b;
                        }
                        /**
                         * @dev Returns the smallest of two numbers.
                         */
                        function min(uint256 a, uint256 b) internal pure returns (uint256) {
                            return a < b ? a : b;
                        }
                        /**
                         * @dev Returns the average of two numbers. The result is rounded towards
                         * zero.
                         */
                        function average(uint256 a, uint256 b) internal pure returns (uint256) {
                            // (a + b) / 2 can overflow.
                            return (a & b) + (a ^ b) / 2;
                        }
                        /**
                         * @dev Returns the ceiling of the division of two numbers.
                         *
                         * This differs from standard division with `/` in that it rounds up instead
                         * of rounding down.
                         */
                        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                            // (a + b - 1) / b can overflow on addition, so we distribute.
                            return a == 0 ? 0 : (a - 1) / b + 1;
                        }
                        /**
                         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                         * with further edits by Uniswap Labs also under MIT license.
                         */
                        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                            unchecked {
                                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                                // variables such that product = prod1 * 2^256 + prod0.
                                uint256 prod0; // Least significant 256 bits of the product
                                uint256 prod1; // Most significant 256 bits of the product
                                assembly {
                                    let mm := mulmod(x, y, not(0))
                                    prod0 := mul(x, y)
                                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                }
                                // Handle non-overflow cases, 256 by 256 division.
                                if (prod1 == 0) {
                                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                                    // The surrounding unchecked block does not change this fact.
                                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                                    return prod0 / denominator;
                                }
                                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                require(denominator > prod1, "Math: mulDiv overflow");
                                ///////////////////////////////////////////////
                                // 512 by 256 division.
                                ///////////////////////////////////////////////
                                // Make division exact by subtracting the remainder from [prod1 prod0].
                                uint256 remainder;
                                assembly {
                                    // Compute remainder using mulmod.
                                    remainder := mulmod(x, y, denominator)
                                    // Subtract 256 bit number from 512 bit number.
                                    prod1 := sub(prod1, gt(remainder, prod0))
                                    prod0 := sub(prod0, remainder)
                                }
                                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                                // See https://cs.stackexchange.com/q/138556/92363.
                                // Does not overflow because the denominator cannot be zero at this stage in the function.
                                uint256 twos = denominator & (~denominator + 1);
                                assembly {
                                    // Divide denominator by twos.
                                    denominator := div(denominator, twos)
                                    // Divide [prod1 prod0] by twos.
                                    prod0 := div(prod0, twos)
                                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                                    twos := add(div(sub(0, twos), twos), 1)
                                }
                                // Shift in bits from prod1 into prod0.
                                prod0 |= prod1 * twos;
                                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                                // four bits. That is, denominator * inv = 1 mod 2^4.
                                uint256 inverse = (3 * denominator) ^ 2;
                                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                                // in modular arithmetic, doubling the correct bits in each step.
                                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                                // is no longer required.
                                result = prod0 * inverse;
                                return result;
                            }
                        }
                        /**
                         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                         */
                        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                            uint256 result = mulDiv(x, y, denominator);
                            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                                result += 1;
                            }
                            return result;
                        }
                        /**
                         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                         *
                         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                         */
                        function sqrt(uint256 a) internal pure returns (uint256) {
                            if (a == 0) {
                                return 0;
                            }
                            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                            //
                            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                            //
                            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                            //
                            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                            uint256 result = 1 << (log2(a) >> 1);
                            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                            // into the expected uint128 result.
                            unchecked {
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                result = (result + a / result) >> 1;
                                return min(result, a / result);
                            }
                        }
                        /**
                         * @notice Calculates sqrt(a), following the selected rounding direction.
                         */
                        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = sqrt(a);
                                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 2, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log2(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >> 128 > 0) {
                                    value >>= 128;
                                    result += 128;
                                }
                                if (value >> 64 > 0) {
                                    value >>= 64;
                                    result += 64;
                                }
                                if (value >> 32 > 0) {
                                    value >>= 32;
                                    result += 32;
                                }
                                if (value >> 16 > 0) {
                                    value >>= 16;
                                    result += 16;
                                }
                                if (value >> 8 > 0) {
                                    value >>= 8;
                                    result += 8;
                                }
                                if (value >> 4 > 0) {
                                    value >>= 4;
                                    result += 4;
                                }
                                if (value >> 2 > 0) {
                                    value >>= 2;
                                    result += 2;
                                }
                                if (value >> 1 > 0) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log2(value);
                                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 10, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log10(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >= 10 ** 64) {
                                    value /= 10 ** 64;
                                    result += 64;
                                }
                                if (value >= 10 ** 32) {
                                    value /= 10 ** 32;
                                    result += 32;
                                }
                                if (value >= 10 ** 16) {
                                    value /= 10 ** 16;
                                    result += 16;
                                }
                                if (value >= 10 ** 8) {
                                    value /= 10 ** 8;
                                    result += 8;
                                }
                                if (value >= 10 ** 4) {
                                    value /= 10 ** 4;
                                    result += 4;
                                }
                                if (value >= 10 ** 2) {
                                    value /= 10 ** 2;
                                    result += 2;
                                }
                                if (value >= 10 ** 1) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log10(value);
                                return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                            }
                        }
                        /**
                         * @dev Return the log in base 256, rounded down, of a positive value.
                         * Returns 0 if given 0.
                         *
                         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                         */
                        function log256(uint256 value) internal pure returns (uint256) {
                            uint256 result = 0;
                            unchecked {
                                if (value >> 128 > 0) {
                                    value >>= 128;
                                    result += 16;
                                }
                                if (value >> 64 > 0) {
                                    value >>= 64;
                                    result += 8;
                                }
                                if (value >> 32 > 0) {
                                    value >>= 32;
                                    result += 4;
                                }
                                if (value >> 16 > 0) {
                                    value >>= 16;
                                    result += 2;
                                }
                                if (value >> 8 > 0) {
                                    result += 1;
                                }
                            }
                            return result;
                        }
                        /**
                         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                         * Returns 0 if given 0.
                         */
                        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                            unchecked {
                                uint256 result = log256(value);
                                return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                            }
                        }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    interface RocketStorageInterface {
                        // Deploy status
                        function getDeployedStatus() external view returns (bool);
                        // Guardian
                        function getGuardian() external view returns(address);
                        function setGuardian(address _newAddress) external;
                        function confirmGuardian() external;
                        // Getters
                        function getAddress(bytes32 _key) external view returns (address);
                        function getUint(bytes32 _key) external view returns (uint);
                        function getString(bytes32 _key) external view returns (string memory);
                        function getBytes(bytes32 _key) external view returns (bytes memory);
                        function getBool(bytes32 _key) external view returns (bool);
                        function getInt(bytes32 _key) external view returns (int);
                        function getBytes32(bytes32 _key) external view returns (bytes32);
                        // Setters
                        function setAddress(bytes32 _key, address _value) external;
                        function setUint(bytes32 _key, uint _value) external;
                        function setString(bytes32 _key, string calldata _value) external;
                        function setBytes(bytes32 _key, bytes calldata _value) external;
                        function setBool(bytes32 _key, bool _value) external;
                        function setInt(bytes32 _key, int _value) external;
                        function setBytes32(bytes32 _key, bytes32 _value) external;
                        // Deleters
                        function deleteAddress(bytes32 _key) external;
                        function deleteUint(bytes32 _key) external;
                        function deleteString(bytes32 _key) external;
                        function deleteBytes(bytes32 _key) external;
                        function deleteBool(bytes32 _key) external;
                        function deleteInt(bytes32 _key) external;
                        function deleteBytes32(bytes32 _key) external;
                        // Arithmetic
                        function addUint(bytes32 _key, uint256 _amount) external;
                        function subUint(bytes32 _key, uint256 _amount) external;
                        // Protected storage
                        function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address);
                        function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external;
                        function confirmWithdrawalAddress(address _nodeAddress) external;
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    pragma solidity >0.5.0 <0.9.0;
                    // SPDX-License-Identifier: GPL-3.0-only
                    import "../interface/RocketStorageInterface.sol";
                    /// @title Base settings / modifiers for each contract in Rocket Pool
                    /// @author David Rugendyke
                    abstract contract RocketBase {
                        // Calculate using this as the base
                        uint256 constant calcBase = 1 ether;
                        // Version of the contract
                        uint8 public version;
                        // The main storage contract where primary persistant storage is maintained
                        RocketStorageInterface rocketStorage = RocketStorageInterface(address(0));
                        /*** Modifiers **********************************************************/
                        /**
                        * @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
                        */
                        modifier onlyLatestNetworkContract() {
                            require(getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))), "Invalid or outdated network contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that doesn't match one of the supplied contract or is the latest version of that contract
                        */
                        modifier onlyLatestContract(string memory _contractName, address _contractAddress) {
                            require(_contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", _contractName))), "Invalid or outdated contract");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered node
                        */
                        modifier onlyRegisteredNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a trusted node DAO member
                        */
                        modifier onlyTrustedNode(address _nodeAddress) {
                            require(getBool(keccak256(abi.encodePacked("dao.trustednodes.", "member", _nodeAddress))), "Invalid trusted node");
                            _;
                        }
                        /**
                        * @dev Throws if called by any sender that isn't a registered minipool
                        */
                        modifier onlyRegisteredMinipool(address _minipoolAddress) {
                            require(getBool(keccak256(abi.encodePacked("minipool.exists", _minipoolAddress))), "Invalid minipool");
                            _;
                        }
                        
                        /**
                        * @dev Throws if called by any account other than a guardian account (temporary account allowed access to settings before DAO is fully enabled)
                        */
                        modifier onlyGuardian() {
                            require(msg.sender == rocketStorage.getGuardian(), "Account is not a temporary guardian");
                            _;
                        }
                        /*** Methods **********************************************************/
                        /// @dev Set the main Rocket Storage address
                        constructor(RocketStorageInterface _rocketStorageAddress) {
                            // Update the contract address
                            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
                        }
                        /// @dev Get the address of a network contract by name
                        function getContractAddress(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Check it
                            require(contractAddress != address(0x0), "Contract not found");
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)
                        function getContractAddressUnsafe(string memory _contractName) internal view returns (address) {
                            // Get the current contract address
                            address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
                            // Return
                            return contractAddress;
                        }
                        /// @dev Get the name of a network contract by address
                        function getContractName(address _contractAddress) internal view returns (string memory) {
                            // Get the contract name
                            string memory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
                            // Check it
                            require(bytes(contractName).length > 0, "Contract not found");
                            // Return
                            return contractName;
                        }
                        /// @dev Get revert error message from a .call method
                        function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return "Transaction reverted silently";
                            assembly {
                                // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string)); // All that remains is the revert string
                        }
                        /*** Rocket Storage Methods ****************************************/
                        // Note: Unused helpers have been removed to keep contract sizes down
                        /// @dev Storage get methods
                        function getAddress(bytes32 _key) internal view returns (address) { return rocketStorage.getAddress(_key); }
                        function getUint(bytes32 _key) internal view returns (uint) { return rocketStorage.getUint(_key); }
                        function getString(bytes32 _key) internal view returns (string memory) { return rocketStorage.getString(_key); }
                        function getBytes(bytes32 _key) internal view returns (bytes memory) { return rocketStorage.getBytes(_key); }
                        function getBool(bytes32 _key) internal view returns (bool) { return rocketStorage.getBool(_key); }
                        function getInt(bytes32 _key) internal view returns (int) { return rocketStorage.getInt(_key); }
                        function getBytes32(bytes32 _key) internal view returns (bytes32) { return rocketStorage.getBytes32(_key); }
                        /// @dev Storage set methods
                        function setAddress(bytes32 _key, address _value) internal { rocketStorage.setAddress(_key, _value); }
                        function setUint(bytes32 _key, uint _value) internal { rocketStorage.setUint(_key, _value); }
                        function setString(bytes32 _key, string memory _value) internal { rocketStorage.setString(_key, _value); }
                        function setBytes(bytes32 _key, bytes memory _value) internal { rocketStorage.setBytes(_key, _value); }
                        function setBool(bytes32 _key, bool _value) internal { rocketStorage.setBool(_key, _value); }
                        function setInt(bytes32 _key, int _value) internal { rocketStorage.setInt(_key, _value); }
                        function setBytes32(bytes32 _key, bytes32 _value) internal { rocketStorage.setBytes32(_key, _value); }
                        /// @dev Storage delete methods
                        function deleteAddress(bytes32 _key) internal { rocketStorage.deleteAddress(_key); }
                        function deleteUint(bytes32 _key) internal { rocketStorage.deleteUint(_key); }
                        function deleteString(bytes32 _key) internal { rocketStorage.deleteString(_key); }
                        function deleteBytes(bytes32 _key) internal { rocketStorage.deleteBytes(_key); }
                        function deleteBool(bytes32 _key) internal { rocketStorage.deleteBool(_key); }
                        function deleteInt(bytes32 _key) internal { rocketStorage.deleteInt(_key); }
                        function deleteBytes32(bytes32 _key) internal { rocketStorage.deleteBytes32(_key); }
                        /// @dev Storage arithmetic methods
                        function addUint(bytes32 _key, uint256 _amount) internal { rocketStorage.addUint(_key, _amount); }
                        function subUint(bytes32 _key, uint256 _amount) internal { rocketStorage.subUint(_key, _amount); }
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity >0.5.0 <0.9.0;
                    struct Checkpoint224 {
                        uint32 _block;
                        uint224 _value;
                    }
                    /// @notice Accounting for snapshotting of values based on block numbers
                    interface RocketNetworkSnapshotsInterface {
                        function push(bytes32 _key, uint224 _value) external;
                        function length(bytes32 _key) external view returns (uint256);
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224);
                        function latestBlock(bytes32 _key) external view returns (uint32);
                        function latestValue(bytes32 _key) external view returns (uint224);
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224);
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224);
                    }
                    /**
                       *       .
                       *      / \\
                       *     |.'.|
                       *     |'.'|
                       *   ,'|   |'.
                       *  |,-'-|-'-.|
                       *   __|_| |         _        _      _____           _
                       *  | ___ \\|        | |      | |    | ___ \\         | |
                       *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
                       *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
                       *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
                       *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
                       * +---------------------------------------------------+
                       * |    DECENTRALISED STAKING PROTOCOL FOR ETHEREUM    |
                       * +---------------------------------------------------+
                       *
                       *  Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
                       *  be community-owned, decentralised, permissionless, & trustless.
                       *
                       *  For more information about Rocket Pool, visit https://rocketpool.net
                       *
                       *  Authored by the Rocket Pool Core Team
                       *  Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
                       *  A special thanks to the Rocket Pool community for all their contributions.
                       *
                       */
                    // SPDX-License-Identifier: MIT
                    // Copyright (c) 2016-2023 zOS Global Limited and contributors
                    // Adapted from OpenZeppelin `Checkpoints` contract
                    pragma solidity 0.8.18;
                    import "@openzeppelin4/contracts/utils/math/Math.sol";
                    import "../RocketBase.sol";
                    import "../../interface/network/RocketNetworkSnapshotsInterface.sol";
                    /// @notice Accounting for snapshotting of values based on block numbers
                    contract RocketNetworkSnapshots is RocketBase, RocketNetworkSnapshotsInterface {
                        constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
                            // Set contract version
                            version = 1;
                        }
                        function push(bytes32 _key, uint224 _value) onlyLatestContract("rocketNetworkSnapshots", address(this)) onlyLatestNetworkContract external {
                            _insert(_key, _value);
                        }
                        function length(bytes32 _key) public view returns (uint256) {
                            return rocketStorage.getUint(keccak256(abi.encodePacked("snapshot.length", _key)));
                        }
                        function latest(bytes32 _key) external view returns (bool, uint32, uint224) {
                            uint256 len = length(_key);
                            if (len == 0) {
                                return (false, 0, 0);
                            }
                            Checkpoint224 memory checkpoint = _load(_key, len - 1);
                            return (true, checkpoint._block, checkpoint._value);
                        }
                        function latestBlock(bytes32 _key) external view returns (uint32) {
                            uint256 len = length(_key);
                            return len == 0 ? 0 : _blockAt(_key, len - 1);
                        }
                        function latestValue(bytes32 _key) external view returns (uint224) {
                            uint256 len = length(_key);
                            return len == 0 ? 0 : _valueAt(_key, len - 1);
                        }
                        function lookup(bytes32 _key, uint32 _block) external view returns (uint224) {
                            uint256 len = length(_key);
                            uint256 pos = _binaryLookup(_key, _block, 0, len);
                            return pos == 0 ? 0 : _valueAt(_key, pos - 1);
                        }
                        function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224) {
                            uint256 len = length(_key);
                            uint256 low = 0;
                            uint256 high = len;
                            if (len > 5 && len > _recency) {
                                uint256 mid = len - _recency;
                                if (_block < _blockAt(_key, mid)) {
                                    high = mid;
                                } else {
                                    low = mid + 1;
                                }
                            }
                            uint256 pos = _binaryLookup(_key, _block, low, high);
                            return pos == 0 ? 0 : _valueAt(_key, pos - 1);
                        }
                        function _insert(bytes32 _key, uint224 _value) private {
                            uint32 blockNumber = uint32(block.number);
                            uint256 pos = length(_key);
                            if (pos > 0) {
                                Checkpoint224 memory last = _load(_key, pos - 1);
                                // Checkpoint keys must be non-decreasing.
                                require (last._block <= blockNumber, "Unordered snapshot insertion");
                                // Update or push new checkpoint
                                if (last._block == blockNumber) {
                                    last._value = _value;
                                    _set(_key, pos - 1, last);
                                } else {
                                    _push(_key, Checkpoint224({_block: blockNumber, _value: _value}));
                                }
                            } else {
                                _push(_key, Checkpoint224({_block: blockNumber, _value: _value}));
                            }
                        }
                        function _binaryLookup(
                            bytes32 _key,
                            uint32 _block,
                            uint256 _low,
                            uint256 _high
                        ) private view returns (uint256) {
                            while (_low < _high) {
                                uint256 mid = Math.average(_low, _high);
                                if (_blockAt(_key, mid) > _block) {
                                    _high = mid;
                                } else {
                                    _low = mid + 1;
                                }
                            }
                            return _high;
                        }
                        function _load(bytes32 _key, uint256 _pos) private view returns (Checkpoint224 memory) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            Checkpoint224 memory result;
                            result._block = uint32(uint256(raw) >> 224);
                            result._value = uint224(uint256(raw));
                            return result;
                        }
                        function _blockAt(bytes32 _key, uint256 _pos) private view returns (uint32) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            return uint32(uint256(raw) >> 224);
                        }
                        function _valueAt(bytes32 _key, uint256 _pos) private view returns (uint224) {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            bytes32 raw = rocketStorage.getBytes32(key);
                            return uint224(uint256(raw));
                        }
                        function _push(bytes32 _key, Checkpoint224 memory _item) private {
                            bytes32 lengthKey = keccak256(abi.encodePacked("snapshot.length", _key));
                            uint256 snapshotLength = rocketStorage.getUint(lengthKey);
                            bytes32 key = bytes32(uint256(_key) + snapshotLength);
                            rocketStorage.setUint(lengthKey, snapshotLength + 1);
                            rocketStorage.setBytes32(key, _encode(_item));
                        }
                        function _set(bytes32 _key, uint256 _pos, Checkpoint224 memory _item) private {
                            bytes32 key = bytes32(uint256(_key) + _pos);
                            rocketStorage.setBytes32(key, _encode(_item));
                        }
                        function _encode(Checkpoint224 memory _item) private pure returns (bytes32) {
                            return bytes32(
                                uint256(_item._block) << 224 | uint256(_item._value)
                            );
                        }
                    }