Transaction Hash:
Block:
17789013 at Jul-28-2023 03:56:47 AM +UTC
Transaction Fee:
0.00223296907436869 ETH
$5.46
Gas Used:
124,330 Gas / 17.960018293 Gwei
Emitted Events:
268 |
RocketTokenRPL.Transfer( from=[Sender] 0xa6fb1a1fff573466295ed477219ca1419bc9a400, to=[Receiver] RocketNodeStaking, value=56070147676739744000 )
|
269 |
RocketTokenRPL.Approval( owner=[Sender] 0xa6fb1a1fff573466295ed477219ca1419bc9a400, spender=[Receiver] RocketNodeStaking, value=999999943929852323260256000 )
|
270 |
RocketTokenRPL.Approval( owner=[Receiver] RocketNodeStaking, spender=RocketVault, value=56070147676739744000 )
|
271 |
RocketTokenRPL.Transfer( from=[Receiver] RocketNodeStaking, to=RocketVault, value=56070147676739744000 )
|
272 |
RocketTokenRPL.Approval( owner=[Receiver] RocketNodeStaking, spender=RocketVault, value=0 )
|
273 |
RocketVault.TokenDeposited( by=E2390B72DFDD3EE64764589B670E97CB05DE272B6555CDBF214AB8CE931ADB8A, tokenAddress=RocketTokenRPL, amount=56070147676739744000, time=1690516607 )
|
274 |
RocketNodeStaking.RPLStaked( from=0x23fde06A...31d28Eb43, amount=56070147676739744000, time=1690516607 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x1d8f8f00...b0ee0Fa46 | (Rocket Pool: Storage) | ||||
0x1f9090aa...8e676c326
Miner
| 0.627708996724162258 Eth | 0.627715213224162258 Eth | 0.0000062165 | ||
0x3bDC69C4...636b469d6 | (Rocket Pool: Vault) | ||||
0xa6FB1a1f...19bC9A400 |
0.178001732871526835 Eth
Nonce: 164
|
0.175768763797158145 Eth
Nonce: 165
| 0.00223296907436869 | ||
0xD3352606...04F21A51f |
Execution Trace
RocketNodeStaking.stakeRPLFor( _nodeAddress=0x23fde06A96496CF55F121b55d14c66131d28Eb43, _amount=56070147676739744000 )
-
RocketStorage.getAddress( _key=8FC06385DE84508EAF7EB3D75B93167987C9629589FE0A868A2B4E0E90862DD8 ) => ( r=0x0d8D8f8541B12A0e1194B7CC4b6D954b90AB82ec )
-
RocketStorage.getBool( _key=3C3C20A3D2FF66D2E62AC857CF3F8A972509FC668B89F8CC494632B15FDFDCB8 ) => ( r=True )
-
RocketStorage.getAddress( _key=759D15219B5ADBAF4EC95EE41B49B3F96FCFF9BE0258E4A1CD69074965F237D1 ) => ( r=0x7EcCBbd05830EdF593d30005B8F69E965AF4D59f )
-
RocketStorage.getNodeWithdrawalAddress( _nodeAddress=0x23fde06A96496CF55F121b55d14c66131d28Eb43 ) => ( 0xa6FB1a1fFF573466295ed477219CA1419bC9A400 )
-
RocketStorage.getAddress( _key=1B80652F417157FE4774177F7D33C8B600483B9193E3F00D2AB1ACE8FA6BDC9C ) => ( r=0xD33526068D116cE69F19A9ee46F0bd304F21A51f )
-
RocketStorage.getAddress( _key=41C30D91BFAF5FA8D610263B0554366F2159A2B6807BF2FDBEB8F2B21A62F17B ) => ( r=0x3bDC69C4E5e13E52A65f5583c23EFB9636b469d6 )
-
RocketTokenRPL.transferFrom( sender=0xa6FB1a1fFF573466295ed477219CA1419bC9A400, recipient=0x0d8D8f8541B12A0e1194B7CC4b6D954b90AB82ec, amount=56070147676739744000 ) => ( True )
-
RocketTokenRPL.approve( spender=0x3bDC69C4E5e13E52A65f5583c23EFB9636b469d6, amount=56070147676739744000 ) => ( True )
RocketVault.depositToken( _networkContractName=rocketNodeStaking, _tokenContract=0xD33526068D116cE69F19A9ee46F0bd304F21A51f, _amount=56070147676739744000 )
-
RocketStorage.getAddress( _key=8FC06385DE84508EAF7EB3D75B93167987C9629589FE0A868A2B4E0E90862DD8 ) => ( r=0x0d8D8f8541B12A0e1194B7CC4b6D954b90AB82ec )
-
RocketTokenRPL.transferFrom( sender=0x0d8D8f8541B12A0e1194B7CC4b6D954b90AB82ec, recipient=0x3bDC69C4E5e13E52A65f5583c23EFB9636b469d6, amount=56070147676739744000 ) => ( True )
-
-
RocketStorage.addUint( _key=E9120B7223AE2A37E3E3D2DFB439C54EBBDEABE89BCE6D355F59122854C4A306, _amount=56070147676739744000 )
-
RocketStorage.addUint( _key=4382D200372B196DA9B3C2D9E796914D364E458E64F27F8CFB8B161D0BC42538, _amount=56070147676739744000 )
-
RocketStorage.setUint( _key=51A7497496130974B93079D0DFD38682F4CD267DB2EAD300635DAD927501603E, _value=1690516607 )
stakeRPLFor[RocketNodeStaking (ln:1559)]
getAddress[RocketNodeStaking (ln:1561)]
getNodeWithdrawalAddress[RocketNodeStaking (ln:1562)]
getBool[RocketNodeStaking (ln:1564)]
_stakeRPL[RocketNodeStaking (ln:1567)]
getContractAddress[RocketNodeStaking (ln:1581)]
getContractAddress[RocketNodeStaking (ln:1582)]
transferFrom[RocketNodeStaking (ln:1586)]
approve[RocketNodeStaking (ln:1588)]
depositToken[RocketNodeStaking (ln:1589)]
increaseTotalRPLStake[RocketNodeStaking (ln:1591)]
increaseNodeRPLStake[RocketNodeStaking (ln:1592)]
setNodeRPLStakedTime[RocketNodeStaking (ln:1593)]
RPLStaked[RocketNodeStaking (ln:1595)]
File 1 of 4: RocketNodeStaking
File 2 of 4: RocketTokenRPL
File 3 of 4: RocketVault
File 4 of 4: 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; } } // 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); } /** * . * / \\ * |.'.| * |'.'| * ,'| |`. * |,-'-|-'-.| * __|_| | _ _ _____ _ * | ___ \\| | | | | | ___ \\ | | * | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | | * | // _ \\ / __| |/ / _ \\ __| | __/ _ \\ / _ \\| | * | |\\ \\ (_) | (__| < __/ |_ | | | (_) | (_) | | * \\_| \\_\\___/ \\___|_|\\_\\___|\\__| \\_| \\___/ \\___/|_| * +---------------------------------------------------+ * | 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; // 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 // 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 2.0 | * +---------------------------------------------------+ * * Rocket Pool is a first-of-its-kind ETH2 Proof of Stake protocol, designed to be community owned, * decentralised, trustless and compatible with staking in Ethereum 2.0. * * For more information about Rocket Pool, visit https://rocketpool.net * * Authors: David Rugendyke, Jake Pospischil, Kane Wallmann, Darren Langley, Joe Clapis, Nick Doherty * */ pragma solidity 0.7.6; // SPDX-License-Identifier: GPL-3.0-only // Represents a minipool's status within the network enum MinipoolStatus { Initialised, // The minipool has been initialised and is awaiting a deposit of user ETH Prelaunch, // The minipool has enough ETH to begin staking and is awaiting launch by the node operator Staking, // The minipool is currently staking Withdrawable, // 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 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 "./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 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 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 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/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 incrementNodeFinalisedMinipoolCount(address _nodeAddress) external; function setMinipoolPubkey(bytes calldata _pubkey) external; function getMinipoolDepositType(address _minipoolAddress) external view returns (MinipoolDeposit); } /** * . * / \\ * |.'.| * |'.'| * ,'| |`. * |,-'-|-'-.| * __|_| | _ _ _____ _ * | ___ \\| | | | | | ___ \\ | | * | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | | * | // _ \\ / __| |/ / _ \\ __| | __/ _ \\ / _ \\| | * | |\\ \\ (_) | (__| < __/ |_ | | | (_) | (_) | | * \\_| \\_\\___/ \\___|_|\\_\\___|\\__| \\_| \\___/ \\___/|_| * +---------------------------------------------------+ * | 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 RocketNetworkPricesInterface { function getPricesBlock() external view returns (uint256); function getRPLPrice() external view returns (uint256); function getLatestReportableBlock() external view returns (uint256); function submitPrices(uint256 _block, uint256 _rplPrice) external; function executeUpdatePrices(uint256 _block, uint256 _rplPrice) 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 * */ pragma solidity >0.5.0 <0.9.0; // SPDX-License-Identifier: GPL-3.0-only interface RocketDAOProtocolSettingsRewardsInterface { function setSettingRewardsClaimer(string memory _contractName, uint256 _perc) external; function getRewardsClaimerPerc(string memory _contractName) external view returns (uint256); function getRewardsClaimerPercTimeUpdated(string memory _contractName) external view returns (uint256); function getRewardsClaimersPercTotal() external view returns (uint256); function getRewardsClaimIntervalTime() external view returns (uint256); } /** * . * / \\ * |.'.| * |'.'| * ,'| |`. * |,-'-|-'-.| * __|_| | _ _ _____ _ * | ___ \\| | | | | | ___ \\ | | * | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | | * | // _ \\ / __| |/ / _ \\ __| | __/ _ \\ / _ \\| | * | |\\ \\ (_) | (__| < __/ |_ | | | (_) | (_) | | * \\_| \\_\\___/ \\___|_|\\_\\___|\\__| \\_| \\___/ \\___/|_| * +---------------------------------------------------+ * | 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 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 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 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); } // 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; } } // 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; import "../../utils/Context.sol"; import "./ERC20.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { using SafeMath for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, 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.5.0 <0.9.0; // SPDX-License-Identifier: GPL-3.0-only import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.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(ERC20Burnable _tokenAddress, uint256 _amount) 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 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 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 "@openzeppelin/contracts/token/ERC20/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"; /// @notice Handles node deposits and minipool creation contract RocketNodeStaking is RocketBase, RocketNodeStakingInterface { // Libs using SafeMath for uint; // 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); constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { version = 4; } /// @notice Returns the total quantity of RPL staked on the network function getTotalRPLStake() override external view returns (uint256) { return getUint(keccak256("rpl.staked.total.amount")); } /// @dev Increases the total network RPL stake /// @param _amount How much to increase by function increaseTotalRPLStake(uint256 _amount) private { addUint(keccak256("rpl.staked.total.amount"), _amount); } /// @dev Decrease the total network RPL stake /// @param _amount How much to decrease by function decreaseTotalRPLStake(uint256 _amount) private { subUint(keccak256("rpl.staked.total.amount"), _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) { return getUint(keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress))); } /// @dev Increases a node operator's RPL stake /// @param _amount How much to increase by function increaseNodeRPLStake(address _nodeAddress, uint256 _amount) private { addUint(keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress)), _amount); } /// @dev Decrease a node operator's RPL stake /// @param _amount How much to decrease by function decreaseNodeRPLStake(address _nodeAddress, uint256 _amount) private { subUint(keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress)), _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) { uint256 ethMatched = getUint(keccak256(abi.encodePacked("eth.matched.node.amount", _nodeAddress))); if (ethMatched > 0) { return ethMatched; } else { // Fallback for backwards compatibility before ETH matched was recorded (all minipools matched 16 ETH from protocol) RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager")); return rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress).mul(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 = getUint(keccak256(abi.encodePacked("eth.matched.node.amount", _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.mul(launchAmount); return totalEthStaked.sub(ethMatched); } else { // Fallback for legacy minipools is number of staking minipools * 16 return activeMinipoolCount.mul(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 = getUint(keccak256(abi.encodePacked("eth.matched.node.amount", _nodeAddress))); if (ethMatched == 0) { // Node operator only has legacy minipools and all legacy minipools had a 1:1 ratio return calcBase.mul(2); } else { RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool")); uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance(); RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager")); uint256 totalEthStaked = rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress).mul(launchAmount); return totalEthStaked.mul(calcBase).div(totalEthStaked.sub(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 external 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.mul(maximumStakePercent).div(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.mul(minimumStakePercent).div(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 .mul(minimumStakePercent) .div(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 .mul(maximumStakePercent) .div(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) .mul(rocketNetworkPrices.getRPLPrice()) .div(minimumStakePercent); } /// @notice Accept an RPL stake /// Only accepts calls from registered nodes /// Requires call to have approved this contract to spend RPL /// @param _amount The amount of RPL to stake function stakeRPL(uint256 _amount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredNode(msg.sender) { _stakeRPL(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 external onlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredNode(_nodeAddress) { // Must be node's withdrawal address, allow listed address or rocketMerkleDistributorMainnet if (msg.sender != getAddress(keccak256(abi.encodePacked("contract.address", "rocketMerkleDistributorMainnet")))) { address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress); if (msg.sender != withdrawalAddress) { require(getBool(keccak256(abi.encodePacked("node.stake.for.allowed", _nodeAddress, msg.sender))), "Not allowed to stake for"); } } _stakeRPL(_nodeAddress, _amount); } /// @notice Explicitly allow or remove allowance of an address to be able to stake on behalf of a node /// @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 onlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredNode(msg.sender) { setBool(keccak256(abi.encodePacked("node.stake.for.allowed", msg.sender, _caller)), _allowed); emit StakeRPLForAllowed(msg.sender, _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 Withdraw staked RPL back to the node account /// Only accepts calls from registered nodes /// Withdraws to withdrawal address if set, otherwise defaults to node address /// @param _amount The amount of RPL to withdraw function withdrawRPL(uint256 _amount) override external onlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredNode(msg.sender) { // 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.sub(getNodeRPLStakedTime(msg.sender)) >= rocketDAOProtocolSettingsRewards.getRewardsClaimIntervalTime(), "The withdrawal cooldown period has not passed"); // Get & check node's current RPL stake uint256 rplStake = getNodeRPLStake(msg.sender); require(rplStake >= _amount, "Withdrawal amount exceeds node's staked RPL balance"); // Check withdrawal would not undercollateralize node require(rplStake.sub(_amount) >= getNodeMaximumRPLStake(msg.sender), "Node's staked RPL balance after withdrawal is less than required balance"); // Update RPL stake amounts decreaseTotalRPLStake(_amount); decreaseNodeRPLStake(msg.sender, _amount); // Transfer RPL tokens to node address rocketVault.withdrawToken(rocketStorage.getNodeWithdrawalAddress(msg.sender), IERC20(getContractAddress("rocketTokenRPL")), _amount); // Emit RPL withdrawn event emit RPLWithdrawn(msg.sender, _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.mul(_ethSlashAmount).div(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 2 of 4: RocketTokenRPL
// 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; import "../../utils/Context.sol"; import "./ERC20.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { using SafeMath for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } } // 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/ERC20Burnable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../RocketBase.sol"; import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsInflationInterface.sol"; import "../../interface/token/RocketTokenRPLInterface.sol"; import "../../interface/RocketVaultInterface.sol"; // RPL Governance and utility token // Inlfationary with rate determined by DAO contract RocketTokenRPL is RocketBase, ERC20Burnable, RocketTokenRPLInterface { // Libs using SafeMath for uint; /**** Properties ***********/ // How many RPL tokens minted to date (18m from fixed supply) uint256 constant totalInitialSupply = 18000000000000000000000000; // The RPL inflation interval uint256 constant inflationInterval = 1 days; // How many RPL tokens have been swapped for new ones uint256 public totalSwappedRPL = 0; // Timestamp of last block inflation was calculated at uint256 private inflationCalcTime = 0; /**** Contracts ************/ // The address of our fixed supply RPL ERC20 token contract IERC20 rplFixedSupplyContract = IERC20(address(0)); /**** Events ***********/ event RPLInflationLog(address sender, uint256 value, uint256 inflationCalcTime); event RPLFixedSupplyBurn(address indexed from, uint256 amount, uint256 time); // Construct constructor(RocketStorageInterface _rocketStorageAddress, IERC20 _rocketTokenRPLFixedSupplyAddress) RocketBase(_rocketStorageAddress) ERC20("Rocket Pool Protocol", "RPL") { // Version version = 1; // Set the mainnet RPL fixed supply token address rplFixedSupplyContract = IERC20(_rocketTokenRPLFixedSupplyAddress); // Mint the 18m tokens that currently exist and allow them to be sent to people burning existing fixed supply RPL _mint(address(this), totalInitialSupply); } /** * Get the last time that inflation was calculated at * @return uint256 Last timestamp since inflation was calculated */ function getInflationCalcTime() override public view returns(uint256) { // Get the last time inflation was calculated if it has even started uint256 inflationStartTime = getInflationIntervalStartTime(); // If inflation has just begun but not been calculated previously, use the start block as the last calculated point if it has passed return inflationCalcTime == 0 && inflationStartTime < block.timestamp ? inflationStartTime : inflationCalcTime; } /** * How many seconds to calculate inflation at * @return uint256 how many seconds to calculate inflation at */ function getInflationIntervalTime() override external pure returns(uint256) { return inflationInterval; } /** * The current inflation rate per interval (eg 1000133680617113500 = 5% annual) * @return uint256 The current inflation rate per interval */ function getInflationIntervalRate() override public view returns(uint256) { // Inflation rate controlled by the DAO RocketDAOProtocolSettingsInflationInterface daoSettingsInflation = RocketDAOProtocolSettingsInflationInterface(getContractAddress("rocketDAOProtocolSettingsInflation")); return daoSettingsInflation.getInflationIntervalRate(); } /** * The current block to begin inflation at * @return uint256 The current block to begin inflation at */ function getInflationIntervalStartTime() override public view returns(uint256) { // Inflation rate start time controlled by the DAO RocketDAOProtocolSettingsInflationInterface daoSettingsInflation = RocketDAOProtocolSettingsInflationInterface(getContractAddress("rocketDAOProtocolSettingsInflation")); return daoSettingsInflation.getInflationIntervalStartTime(); } /** * The current rewards pool address that receives the inflation * @return address The rewards pool contract address */ function getInflationRewardsContractAddress() override external view returns(address) { // Inflation rate start block controlled by the DAO return getContractAddress("rocketRewardsPool"); } /** * Compute interval since last inflation update (on call) * @return uint256 Time intervals since last update */ function getInflationIntervalsPassed() override public view returns(uint256) { // The time that inflation was last calculated at uint256 inflationLastCalculatedTime = getInflationCalcTime(); return _getInflationIntervalsPassed(inflationLastCalculatedTime); } function _getInflationIntervalsPassed(uint256 _inflationLastCalcTime) private view returns(uint256) { // Calculate now if inflation has begun if(_inflationLastCalcTime > 0) { return (block.timestamp).sub(_inflationLastCalcTime).div(inflationInterval); }else{ return 0; } } /** * @dev Function to compute how many tokens should be minted * @return A uint256 specifying number of new tokens to mint */ function inflationCalculate() override external view returns (uint256) { uint256 intervalsSinceLastMint = getInflationIntervalsPassed(); return _inflationCalculate(intervalsSinceLastMint); } function _inflationCalculate(uint256 _intervalsSinceLastMint) private view returns (uint256) { // The inflation amount uint256 inflationTokenAmount = 0; // Only update if last interval has passed and inflation rate is > 0 if(_intervalsSinceLastMint > 0) { // Optimisation uint256 inflationRate = getInflationIntervalRate(); if(inflationRate > 0) { // Get the total supply now uint256 totalSupplyCurrent = totalSupply(); uint256 newTotalSupply = totalSupplyCurrent; // Compute inflation for total inflation intervals elapsed for (uint256 i = 0; i < _intervalsSinceLastMint; i++) { newTotalSupply = newTotalSupply.mul(inflationRate).div(10**18); } // Return inflation amount inflationTokenAmount = newTotalSupply.sub(totalSupplyCurrent); } } // Done return inflationTokenAmount; } /** * @dev Mint new tokens if enough time has elapsed since last mint * @return A uint256 specifying number of new tokens that were minted */ function inflationMintTokens() override external returns (uint256) { // Only run inflation process if at least 1 interval has passed (function returns 0 otherwise) uint256 inflationLastCalcTime = getInflationCalcTime(); uint256 intervalsSinceLastMint = _getInflationIntervalsPassed(inflationLastCalcTime); if (intervalsSinceLastMint == 0) { return 0; } // Address of the vault where to send tokens address rocketVaultAddress = getContractAddress("rocketVault"); require(rocketVaultAddress != address(0x0), "rocketVault address not set"); // Only mint if we have new tokens to mint since last interval and an address is set to receive them RocketVaultInterface rocketVaultContract = RocketVaultInterface(rocketVaultAddress); // Calculate the amount of tokens now based on inflation rate uint256 newTokens = _inflationCalculate(intervalsSinceLastMint); // Update last inflation calculation timestamp even if inflation rate is 0 inflationCalcTime = inflationLastCalcTime.add(inflationInterval.mul(intervalsSinceLastMint)); // Check if actually need to mint tokens (e.g. inflation rate > 0) if (newTokens > 0) { // Mint to itself, then allocate tokens for transfer to rewards contract, this will update balance & supply _mint(address(this), newTokens); // Initialise itself and allow from it's own balance (cant just do an allow as it could be any user calling this so they are msg.sender) IERC20 rplInflationContract = IERC20(address(this)); // Get the current allowance for Rocket Vault uint256 vaultAllowance = rplFixedSupplyContract.allowance(rocketVaultAddress, address(this)); // Now allow Rocket Vault to move those tokens, we also need to account of any other allowances for this token from other contracts in the same block require(rplInflationContract.approve(rocketVaultAddress, vaultAllowance.add(newTokens)), "Allowance for Rocket Vault could not be approved"); // Let vault know it can move these tokens to itself now and credit the balance to the RPL rewards pool contract rocketVaultContract.depositToken("rocketRewardsPool", IERC20(address(this)), newTokens); } // Log it emit RPLInflationLog(msg.sender, newTokens, inflationCalcTime); // return number minted return newTokens; } /** * @dev Swap current RPL fixed supply tokens for new RPL 1:1 to the same address from the user calling it * @param _amount The amount of RPL fixed supply tokens to swap */ function swapTokens(uint256 _amount) override external { // Valid amount? require(_amount > 0, "Please enter valid amount of RPL to swap"); // Send the tokens to this contract now and mint new ones for them require(rplFixedSupplyContract.transferFrom(msg.sender, address(this), _amount), "Token transfer from existing RPL contract was not successful"); // Transfer from the contracts RPL balance to the user require(this.transfer(msg.sender, _amount), "Token transfer from RPL inflation contract was not successful"); // Update the total swapped totalSwappedRPL = totalSwappedRPL.add(_amount); // Log it emit RPLFixedSupplyBurn(msg.sender, _amount, 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, 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 import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.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(ERC20Burnable _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, 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 RocketDAOProtocolSettingsInflationInterface { function getInflationIntervalRate() external view returns (uint256); function getInflationIntervalStartTime() 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 import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface RocketTokenRPLInterface is IERC20 { function getInflationCalcTime() external view returns(uint256); function getInflationIntervalTime() external view returns(uint256); function getInflationIntervalRate() external view returns(uint256); function getInflationIntervalsPassed() external view returns(uint256); function getInflationIntervalStartTime() external view returns(uint256); function getInflationRewardsContractAddress() external view returns(address); function inflationCalculate() external view returns (uint256); function inflationMintTokens() external returns (uint256); function swapTokens(uint256 _amount) external; }
File 3 of 4: RocketVault
// 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; import "../../utils/Context.sol"; import "./ERC20.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { using SafeMath for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } } // 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; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // 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 "./RocketBase.sol"; import "../interface/RocketVaultInterface.sol"; import "../interface/RocketVaultWithdrawerInterface.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol"; // ETH and rETH are stored here to prevent contract upgrades from affecting balances // The RocketVault contract must not be upgraded contract RocketVault is RocketBase, RocketVaultInterface { // Libs using SafeMath for uint; using SafeERC20 for IERC20; // Network contract balances mapping(string => uint256) etherBalances; mapping(bytes32 => uint256) tokenBalances; // Events event EtherDeposited(string indexed by, uint256 amount, uint256 time); event EtherWithdrawn(string indexed by, uint256 amount, uint256 time); event TokenDeposited(bytes32 indexed by, address indexed tokenAddress, uint256 amount, uint256 time); event TokenWithdrawn(bytes32 indexed by, address indexed tokenAddress, uint256 amount, uint256 time); event TokenBurned(bytes32 indexed by, address indexed tokenAddress, uint256 amount, uint256 time); event TokenTransfer(bytes32 indexed by, bytes32 indexed to, address indexed tokenAddress, uint256 amount, uint256 time); \t// Construct constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { version = 1; } // Get a contract's ETH balance by address function balanceOf(string memory _networkContractName) override external view returns (uint256) { // Return balance return etherBalances[_networkContractName]; } // Get the balance of a token held by a network contract function balanceOfToken(string memory _networkContractName, IERC20 _tokenAddress) override external view returns (uint256) { // Return balance return tokenBalances[keccak256(abi.encodePacked(_networkContractName, _tokenAddress))]; } // Accept an ETH deposit from a network contract // Only accepts calls from Rocket Pool network contracts function depositEther() override external payable onlyLatestNetworkContract { // Valid amount? require(msg.value > 0, "No valid amount of ETH given to deposit"); // Get contract key string memory contractName = getContractName(msg.sender); // Update contract balance etherBalances[contractName] = etherBalances[contractName].add(msg.value); // Emit ether deposited event emit EtherDeposited(contractName, msg.value, block.timestamp); } // Withdraw an amount of ETH to a network contract // Only accepts calls from Rocket Pool network contracts function withdrawEther(uint256 _amount) override external onlyLatestNetworkContract { // Valid amount? require(_amount > 0, "No valid amount of ETH given to withdraw"); // Get contract key string memory contractName = getContractName(msg.sender); // Check and update contract balance require(etherBalances[contractName] >= _amount, "Insufficient contract ETH balance"); etherBalances[contractName] = etherBalances[contractName].sub(_amount); // Withdraw RocketVaultWithdrawerInterface withdrawer = RocketVaultWithdrawerInterface(msg.sender); withdrawer.receiveVaultWithdrawalETH{value: _amount}(); // Emit ether withdrawn event emit EtherWithdrawn(contractName, _amount, block.timestamp); } // Accept an token deposit and assign its balance to a network contract (saves a large amount of gas this way through not needing a double token transfer via a network contract first) function depositToken(string memory _networkContractName, IERC20 _tokenContract, uint256 _amount) override external { // Valid amount? require(_amount > 0, "No valid amount of tokens given to deposit"); // Make sure the network contract is valid (will throw if not) require(getContractAddress(_networkContractName) != address(0x0), "Not a valid network contract"); // Get contract key bytes32 contractKey = keccak256(abi.encodePacked(_networkContractName, address(_tokenContract))); // Send the tokens to this contract now require(_tokenContract.transferFrom(msg.sender, address(this), _amount), "Token transfer was not successful"); // Update contract balance tokenBalances[contractKey] = tokenBalances[contractKey].add(_amount); // Emit token transfer emit TokenDeposited(contractKey, address(_tokenContract), _amount, block.timestamp); } // Withdraw an amount of a ERC20 token to an address // Only accepts calls from Rocket Pool network contracts function withdrawToken(address _withdrawalAddress, IERC20 _tokenAddress, uint256 _amount) override external onlyLatestNetworkContract { // Valid amount? require(_amount > 0, "No valid amount of tokens given to withdraw"); // Get contract key bytes32 contractKey = keccak256(abi.encodePacked(getContractName(msg.sender), _tokenAddress)); // Update balances tokenBalances[contractKey] = tokenBalances[contractKey].sub(_amount); // Get the token ERC20 instance IERC20 tokenContract = IERC20(_tokenAddress); // Withdraw to the desired address require(tokenContract.transfer(_withdrawalAddress, _amount), "Rocket Vault token withdrawal unsuccessful"); // Emit token withdrawn event emit TokenWithdrawn(contractKey, address(_tokenAddress), _amount, block.timestamp); } // Transfer token from one contract to another // Only accepts calls from Rocket Pool network contracts function transferToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) override external onlyLatestNetworkContract { // Valid amount? require(_amount > 0, "No valid amount of tokens given to transfer"); // Make sure the network contract is valid (will throw if not) require(getContractAddress(_networkContractName) != address(0x0), "Not a valid network contract"); // Get contract keys bytes32 contractKeyFrom = keccak256(abi.encodePacked(getContractName(msg.sender), _tokenAddress)); bytes32 contractKeyTo = keccak256(abi.encodePacked(_networkContractName, _tokenAddress)); // Update balances tokenBalances[contractKeyFrom] = tokenBalances[contractKeyFrom].sub(_amount); tokenBalances[contractKeyTo] = tokenBalances[contractKeyTo].add(_amount); // Emit token withdrawn event emit TokenTransfer(contractKeyFrom, contractKeyTo, address(_tokenAddress), _amount, block.timestamp); } // Burns an amount of a token that implements a burn(uint256) method // Only accepts calls from Rocket Pool network contracts function burnToken(ERC20Burnable _tokenAddress, uint256 _amount) override external onlyLatestNetworkContract { // Get contract key bytes32 contractKey = keccak256(abi.encodePacked(getContractName(msg.sender), _tokenAddress)); // Update balances tokenBalances[contractKey] = tokenBalances[contractKey].sub(_amount); // Get the token ERC20 instance ERC20Burnable tokenContract = ERC20Burnable(_tokenAddress); // Burn the tokens tokenContract.burn(_amount); // Emit token burn event emit TokenBurned(contractKey, address(_tokenAddress), _amount, 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, 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 import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.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(ERC20Burnable _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, 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 RocketVaultWithdrawerInterface { function receiveVaultWithdrawalETH() external payable; }
File 4 of 4: 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; }