ETH Price: $1,614.97 (+1.66%)

Transaction Decoder

Block:
17072446 at Apr-18-2023 08:39:47 AM +UTC
Transaction Fee:
0.002365771737322048 ETH $3.82
Gas Used:
63,844 Gas / 37.055506192 Gwei

Emitted Events:

52 RocketMinipool.DelegateUpgraded( oldDelegate=0xE7E0DDc7...c1c6154C2, newDelegate=0xA347C391...cD08Ac567, time=1681807187 )

Account State Difference:

  Address   Before After State Difference Code
0.976007251764683042 Eth0.976134939764683042 Eth0.000127688
0x909FA102...F713256E1
0xDb7C387A...e6b1964a6
0.309795068976482892 Eth
Nonce: 90
0.307429297239160844 Eth
Nonce: 91
0.002365771737322048

Execution Trace

RocketMinipool.CALL( )
  • RocketStorage.getNodeWithdrawalAddress( _nodeAddress=0xDb7C387Aa3750C33aC17e388D1471aFe6b1964a6 ) => ( 0xfCBEBc836f74716aC7084B385fA40814D07a2d58 )
  • RocketStorage.getAddress( _key=030D229C9FDC22A22D018C55733A2B9958801FFC58EEC381E462F500EB6DD7D6 ) => ( r=0xA347C391bc8f740CAbA37672157c8aAcD08Ac567 )
    File 1 of 2: RocketMinipool
    /**
      *       .
      *      / \\
      *     |.'.|
      *     |'.'|
      *   ,'|   |`.
      *  |,-'-|-'-.|
      *   __|_| |         _        _      _____           _
      *  | ___ \\|        | |      | |    | ___ \\         | |
      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
      * +---------------------------------------------------+
      * |  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 "./RocketMinipoolStorageLayout.sol";
    import "../../interface/RocketStorageInterface.sol";
    import "../../types/MinipoolDeposit.sol";
    import "../../types/MinipoolStatus.sol";
    // An individual minipool in the Rocket Pool network
    contract RocketMinipool is RocketMinipoolStorageLayout {
        // Events
        event EtherReceived(address indexed from, uint256 amount, uint256 time);
        event DelegateUpgraded(address oldDelegate, address newDelegate, uint256 time);
        event DelegateRolledBack(address oldDelegate, address newDelegate, uint256 time);
        // Modifiers
        // Only allow access from the owning node address
        modifier onlyMinipoolOwner() {
            // Only the node operator can upgrade
            address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(nodeAddress);
            require(msg.sender == nodeAddress || msg.sender == withdrawalAddress, "Only the node operator can access this method");
            _;
        }
        // Construct
        constructor(RocketStorageInterface _rocketStorageAddress, address _nodeAddress, MinipoolDeposit _depositType) {
            // Initialise RocketStorage
            require(address(_rocketStorageAddress) != address(0x0), "Invalid storage address");
            rocketStorage = RocketStorageInterface(_rocketStorageAddress);
            // Set storage state to uninitialised
            storageState = StorageState.Uninitialised;
            // Set the current delegate
            address delegateAddress = getContractAddress("rocketMinipoolDelegate");
            rocketMinipoolDelegate = delegateAddress;
            // Check for contract existence
            require(contractExists(delegateAddress), "Delegate contract does not exist");
            // Call initialise on delegate
            (bool success, bytes memory data) = delegateAddress.delegatecall(abi.encodeWithSignature('initialise(address,uint8)', _nodeAddress, uint8(_depositType)));
            if (!success) { revert(getRevertMessage(data)); }
        }
        // Receive an ETH deposit
        receive() external payable {
            // Emit ether received event
            emit EtherReceived(msg.sender, msg.value, block.timestamp);
        }
        // Upgrade this minipool to the latest network delegate contract
        function delegateUpgrade() external onlyMinipoolOwner {
            // Set previous address
            rocketMinipoolDelegatePrev = rocketMinipoolDelegate;
            // Set new delegate
            rocketMinipoolDelegate = getContractAddress("rocketMinipoolDelegate");
            // Verify
            require(rocketMinipoolDelegate != rocketMinipoolDelegatePrev, "New delegate is the same as the existing one");
            // Log event
            emit DelegateUpgraded(rocketMinipoolDelegatePrev, rocketMinipoolDelegate, block.timestamp);
        }
        // Rollback to previous delegate contract
        function delegateRollback() external onlyMinipoolOwner {
            // Make sure they have upgraded before
            require(rocketMinipoolDelegatePrev != address(0x0), "Previous delegate contract is not set");
            // Store original
            address originalDelegate = rocketMinipoolDelegate;
            // Update delegate to previous and zero out previous
            rocketMinipoolDelegate = rocketMinipoolDelegatePrev;
            rocketMinipoolDelegatePrev = address(0x0);
            // Log event
            emit DelegateRolledBack(originalDelegate, rocketMinipoolDelegate, block.timestamp);
        }
        // If set to true, will automatically use the latest delegate contract
        function setUseLatestDelegate(bool _setting) external onlyMinipoolOwner {
            useLatestDelegate = _setting;
        }
        // Getter for useLatestDelegate setting
        function getUseLatestDelegate() external view returns (bool) {
            return useLatestDelegate;
        }
        // Returns the address of the minipool's stored delegate
        function getDelegate() external view returns (address) {
            return rocketMinipoolDelegate;
        }
        // Returns the address of the minipool's previous delegate (or address(0) if not set)
        function getPreviousDelegate() external view returns (address) {
            return rocketMinipoolDelegatePrev;
        }
        // Returns the delegate which will be used when calling this minipool taking into account useLatestDelegate setting
        function getEffectiveDelegate() external view returns (address) {
            return useLatestDelegate ? getContractAddress("rocketMinipoolDelegate") : rocketMinipoolDelegate;
        }
        // Delegate all other calls to minipool delegate contract
        fallback(bytes calldata _input) external payable returns (bytes memory) {
            // If useLatestDelegate is set, use the latest delegate contract
            address delegateContract = useLatestDelegate ? getContractAddress("rocketMinipoolDelegate") : rocketMinipoolDelegate;
            // Check for contract existence
            require(contractExists(delegateContract), "Delegate contract does not exist");
            // Execute delegatecall
            (bool success, bytes memory data) = delegateContract.delegatecall(_input);
            if (!success) { revert(getRevertMessage(data)); }
            return data;
        }
        // Get the address of a Rocket Pool network contract
        function getContractAddress(string memory _contractName) private view returns (address) {
            address contractAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
            require(contractAddress != address(0x0), "Contract not found");
            return contractAddress;
        }
        // Get a revert message from delegatecall return data
        function getRevertMessage(bytes memory _returnData) private pure returns (string memory) {
            if (_returnData.length < 68) { return "Transaction reverted silently"; }
            assembly {
                _returnData := add(_returnData, 0x04)
            }
            return abi.decode(_returnData, (string));
        }
        // Returns true if contract exists at _contractAddress (if called during that contract's construction it will return a false negative)
        function contractExists(address _contractAddress) private returns (bool) {
            uint32 codeSize;
            assembly {
                codeSize := extcodesize(_contractAddress)
            }
            return codeSize > 0;
        }
    }
    /**
      *       .
      *      / \\
      *     |.'.|
      *     |'.'|
      *   ,'|   |`.
      *  |,-'-|-'-.|
      *   __|_| |         _        _      _____           _
      *  | ___ \\|        | |      | |    | ___ \\         | |
      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
      * +---------------------------------------------------+
      * |  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";
    import "../../types/MinipoolDeposit.sol";
    import "../../types/MinipoolStatus.sol";
    // The RocketMinipool contract storage layout, shared by RocketMinipoolDelegate
    // ******************************************************
    // Note: This contract MUST NOT BE UPDATED after launch.
    // All deployed minipool contracts must maintain a
    // Consistent storage layout with RocketMinipoolDelegate.
    // ******************************************************
    abstract contract RocketMinipoolStorageLayout {
        // Storage state enum
        enum StorageState {
            Undefined,
            Uninitialised,
            Initialised
        }
    \t// Main Rocket Pool storage contract
        RocketStorageInterface internal rocketStorage = RocketStorageInterface(0);
        // Status
        MinipoolStatus internal status;
        uint256 internal statusBlock;
        uint256 internal statusTime;
        uint256 internal withdrawalBlock;
        // Deposit type
        MinipoolDeposit internal depositType;
        // Node details
        address internal nodeAddress;
        uint256 internal nodeFee;
        uint256 internal nodeDepositBalance;
        bool internal nodeDepositAssigned;
        uint256 internal nodeRefundBalance;
        uint256 internal nodeSlashBalance;
        // User deposit details
        uint256 internal userDepositBalance;
        uint256 internal userDepositAssignedTime;
        // Upgrade options
        bool internal useLatestDelegate = false;
        address internal rocketMinipoolDelegate;
        address internal rocketMinipoolDelegatePrev;
        // Local copy of RETH address
        address internal rocketTokenRETH;
        // Local copy of penalty contract
        address internal rocketMinipoolPenalty;
        // Used to prevent direct access to delegate and prevent calling initialise more than once
        StorageState storageState = StorageState.Undefined;
        // Whether node operator has finalised the pool
        bool internal finalised;
        // Trusted member scrub votes
        mapping(address => bool) memberScrubVotes;
        uint256 totalScrubVotes;
    }
    /**
      *       .
      *      / \\
      *     |.'.|
      *     |'.'|
      *   ,'|   |`.
      *  |,-'-|-'-.|
      *   __|_| |         _        _      _____           _
      *  | ___ \\|        | |      | |    | ___ \\         | |
      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
      * +---------------------------------------------------+
      * |  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 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;
    // 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)
    }
    /**
      *       .
      *      / \\
      *     |.'.|
      *     |'.'|
      *   ,'|   |`.
      *  |,-'-|-'-.|
      *   __|_| |         _        _      _____           _
      *  | ___ \\|        | |      | |    | ___ \\         | |
      *  | |_/ /|__   ___| | _____| |_   | |_/ /__   ___ | |
      *  |    // _ \\ / __| |/ / _ \\ __|  |  __/ _ \\ / _ \\| |
      *  | |\\ \\ (_) | (__|   <  __/ |_   | | | (_) | (_) | |
      *  \\_| \\_\\___/ \\___|_|\\_\\___|\\__|  \\_|  \\___/ \\___/|_|
      * +---------------------------------------------------+
      * |  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
    // 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,   // The minipool has become withdrawable on the beacon chain and can be withdrawn from by the node operator
        Dissolved       // The minipool has been dissolved and its user deposited ETH has been returned to the deposit pool
    }
    

    File 2 of 2: 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;
    }