Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x18845fD4...f85dEf683 |
0.186001252853295549 Eth
Nonce: 146
|
0.185401492853295549 Eth
Nonce: 147
| 0.00059976 | ||
0x5eA25ecF...deB849074 | (Fee Recipient: 0x5ea2...074) | 0.077787594136672562 Eth | 0 Eth | 0.077787594136672562 | |
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 9.432151444513164955 Eth | 9.432162677623622755 Eth | 0.0000112331104578 | |
0xae78736C...E74Fc6393 | 4,268.831949465583361748 Eth | 4,268.882122463801515551 Eth | 0.050172998218153803 | ||
0xB006C9F5...0788f2873 | 0.416554119597835816 Eth | 0.444168715516354575 Eth | 0.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( )
-
getAddress[RocketStorage (ln:353)]
File 1 of 10: RocketNodeDistributor
File 2 of 10: RocketTokenRETH
File 3 of 10: RocketStorage
File 4 of 10: RocketNodeDistributorDelegate
File 5 of 10: RocketNodeManager
File 6 of 10: RocketNodeDeposit
File 7 of 10: RocketDAOProtocolSettingsMinipool
File 8 of 10: RocketMinipoolManager
File 9 of 10: RocketNodeStaking
File 10 of 10: RocketNetworkSnapshots
/** * . * / \\ * |.'.| * |'.'| * ,'| |`. * |,-'-|-'-.| * __|_| | _ _ _____ _ * | ___ \\| | | | | | ___ \\ | | * | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | | * | // _ \\ / __| |/ / _ \\ __| | __/ _ \\ / _ \\| | * | |\\ \\ (_) | (__| < __/ |_ | | | (_) | (_) | | * \\_| \\_\\___/ \\___|_|\\_\\___|\\__| \\_| \\___/ \\___/|_| * +---------------------------------------------------+ * | 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) ); } }