/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/pragmasolidity >0.5.0 <0.9.0;// SPDX-License-Identifier: GPL-3.0-onlyinterfaceAddressSetStorageInterface{
functiongetCount(bytes32 _key) externalviewreturns (uint);
functiongetItem(bytes32 _key, uint _index) externalviewreturns (address);
functiongetIndexOf(bytes32 _key, address _value) externalviewreturns (int);
functionaddItem(bytes32 _key, address _value) external;
functionremoveItem(bytes32 _key, address _value) external;
}
Contract Source Code
File 2 of 22: IERC20.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)pragmasolidity >0.5.0 <0.9.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed 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.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 amount) externalreturns (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.
*/functionallowance(address owner, address spender) externalviewreturns (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.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom, address to, uint256 amount) externalreturns (bool);
}
Contract Source Code
File 3 of 22: IERC20Burnable.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)import"./IERC20.sol";
pragmasolidity >0.5.0 <0.9.0;interfaceIERC20BurnableisIERC20{
functionburn(uint256 amount) external;
functionburnFrom(address account, uint256 amount) external;
}
Contract Source Code
File 4 of 22: Math.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)pragmasolidity ^0.8.0;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
enumRounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b) / 2 can overflow.return (a & b) + (a ^ b) /2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.return a ==0 ? 0 : (a -1) / b +1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/functionmulDiv(uint256 x, uint256 y, uint256 denominator) internalpurereturns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256// variables such that product = prod1 * 2^256 + prod0.uint256 prod0; // Least significant 256 bits of the productuint256 prod1; // Most significant 256 bits of the productassembly {
let mm :=mulmod(x, y, not(0))
prod0 :=mul(x, y)
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.if (prod1 ==0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.// The surrounding unchecked block does not change this fact.// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////// 512 by 256 division.///////////////////////////////////////////////// Make division exact by subtracting the remainder from [prod1 prod0].uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.// See https://cs.stackexchange.com/q/138556/92363.// Does not overflow because the denominator cannot be zero at this stage in the function.uint256 twos = denominator & (~denominator +1);
assembly {
// Divide denominator by twos.
denominator :=div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 :=div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos :=add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for// four bits. That is, denominator * inv = 1 mod 2^4.uint256 inverse = (3* denominator) ^2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works// in modular arithmetic, doubling the correct bits in each step.
inverse *=2- denominator * inverse; // inverse mod 2^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // inverse mod 2^256// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/functionmulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internalpurereturns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up &&mulmod(x, y, denominator) >0) {
result +=1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.//// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.//// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`//// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.uint256 result =1<< (log2(a) >>1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision// into the expected uint128 result.unchecked {
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/functionsqrt(uint256 a, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=128;
}
if (value >>64>0) {
value >>=64;
result +=64;
}
if (value >>32>0) {
value >>=32;
result +=32;
}
if (value >>16>0) {
value >>=16;
result +=16;
}
if (value >>8>0) {
value >>=8;
result +=8;
}
if (value >>4>0) {
value >>=4;
result +=4;
}
if (value >>2>0) {
value >>=2;
result +=2;
}
if (value >>1>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result =log2(value);
return result + (rounding == Rounding.Up &&1<< result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >=10**64) {
value /=10**64;
result +=64;
}
if (value >=10**32) {
value /=10**32;
result +=32;
}
if (value >=10**16) {
value /=10**16;
result +=16;
}
if (value >=10**8) {
value /=10**8;
result +=8;
}
if (value >=10**4) {
value /=10**4;
result +=4;
}
if (value >=10**2) {
value /=10**2;
result +=2;
}
if (value >=10**1) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up &&10** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/functionlog256(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=16;
}
if (value >>64>0) {
value >>=64;
result +=8;
}
if (value >>32>0) {
value >>=32;
result +=4;
}
if (value >>16>0) {
value >>=16;
result +=2;
}
if (value >>8>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog256(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up &&1<< (result <<3) < value ? 1 : 0);
}
}
}
Contract Source Code
File 5 of 22: MinipoolDeposit.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/pragmasolidity >0.5.0 <0.9.0;// SPDX-License-Identifier: GPL-3.0-only// Represents the type of deposits required by a minipoolenumMinipoolDeposit {
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
}
Contract Source Code
File 6 of 22: MinipoolDetails.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/pragmasolidity >0.5.0 <0.9.0;// SPDX-License-Identifier: GPL-3.0-onlyimport"./MinipoolDeposit.sol";
import"./MinipoolStatus.sol";
// A struct containing all the information on-chain about a specific minipoolstructMinipoolDetails {
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;
}
Contract Source Code
File 7 of 22: MinipoolStatus.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/pragmasolidity >0.5.0 <0.9.0;// SPDX-License-Identifier: GPL-3.0-only// Represents a minipool's status within the networkenumMinipoolStatus {
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
}
Contract Source Code
File 8 of 22: NodeDetails.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/pragmasolidity >0.5.0 <0.9.0;// SPDX-License-Identifier: GPL-3.0-only// A struct containing all the information on-chain about a specific nodestructNodeDetails {
bool exists;
uint256 registrationTime;
string timezoneLocation;
bool feeDistributorInitialised;
address feeDistributorAddress;
uint256 rewardNetwork;
uint256 rplStake;
uint256 effectiveRPLStake;
uint256 minimumRPLStake;
uint256 maximumRPLStake;
uint256 ethMatched;
uint256 ethMatchedLimit;
uint256 minipoolCount;
uint256 balanceETH;
uint256 balanceRETH;
uint256 balanceRPL;
uint256 balanceOldRPL;
uint256 depositCreditBalance;
uint256 distributorBalanceUserETH;
uint256 distributorBalanceNodeETH;
address withdrawalAddress;
address pendingWithdrawalAddress;
bool smoothingPoolRegistrationState;
uint256 smoothingPoolRegistrationChanged;
address nodeAddress;
}
Contract Source Code
File 9 of 22: RocketBase.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/pragmasolidity >0.5.0 <0.9.0;// SPDX-License-Identifier: GPL-3.0-onlyimport"../interface/RocketStorageInterface.sol";
/// @title Base settings / modifiers for each contract in Rocket Pool/// @author David RugendykeabstractcontractRocketBase{
// Calculate using this as the baseuint256constant calcBase =1ether;
// Version of the contractuint8public version;
// The main storage contract where primary persistant storage is maintained
RocketStorageInterface rocketStorage = RocketStorageInterface(address(0));
/*** Modifiers **********************************************************//**
* @dev Throws if called by any sender that doesn't match a Rocket Pool network contract
*/modifieronlyLatestNetworkContract() {
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
*/modifieronlyLatestContract(stringmemory _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
*/modifieronlyRegisteredNode(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
*/modifieronlyTrustedNode(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
*/modifieronlyRegisteredMinipool(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)
*/modifieronlyGuardian() {
require(msg.sender== rocketStorage.getGuardian(), "Account is not a temporary guardian");
_;
}
/*** Methods **********************************************************//// @dev Set the main Rocket Storage addressconstructor(RocketStorageInterface _rocketStorageAddress) {
// Update the contract address
rocketStorage = RocketStorageInterface(_rocketStorageAddress);
}
/// @dev Get the address of a network contract by namefunctiongetContractAddress(stringmemory _contractName) internalviewreturns (address) {
// Get the current contract addressaddress contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
// Check itrequire(contractAddress !=address(0x0), "Contract not found");
// Returnreturn contractAddress;
}
/// @dev Get the address of a network contract by name (returns address(0x0) instead of reverting if contract does not exist)functiongetContractAddressUnsafe(stringmemory _contractName) internalviewreturns (address) {
// Get the current contract addressaddress contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", _contractName)));
// Returnreturn contractAddress;
}
/// @dev Get the name of a network contract by addressfunctiongetContractName(address _contractAddress) internalviewreturns (stringmemory) {
// Get the contract namestringmemory contractName = getString(keccak256(abi.encodePacked("contract.name", _contractAddress)));
// Check itrequire(bytes(contractName).length>0, "Contract not found");
// Returnreturn contractName;
}
/// @dev Get revert error message from a .call methodfunctiongetRevertMsg(bytesmemory _returnData) internalpurereturns (stringmemory) {
// 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)
}
returnabi.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 methodsfunctiongetAddress(bytes32 _key) internalviewreturns (address) { return rocketStorage.getAddress(_key); }
functiongetUint(bytes32 _key) internalviewreturns (uint) { return rocketStorage.getUint(_key); }
functiongetString(bytes32 _key) internalviewreturns (stringmemory) { return rocketStorage.getString(_key); }
functiongetBytes(bytes32 _key) internalviewreturns (bytesmemory) { return rocketStorage.getBytes(_key); }
functiongetBool(bytes32 _key) internalviewreturns (bool) { return rocketStorage.getBool(_key); }
functiongetInt(bytes32 _key) internalviewreturns (int) { return rocketStorage.getInt(_key); }
functiongetBytes32(bytes32 _key) internalviewreturns (bytes32) { return rocketStorage.getBytes32(_key); }
/// @dev Storage set methodsfunctionsetAddress(bytes32 _key, address _value) internal{ rocketStorage.setAddress(_key, _value); }
functionsetUint(bytes32 _key, uint _value) internal{ rocketStorage.setUint(_key, _value); }
functionsetString(bytes32 _key, stringmemory _value) internal{ rocketStorage.setString(_key, _value); }
functionsetBytes(bytes32 _key, bytesmemory _value) internal{ rocketStorage.setBytes(_key, _value); }
functionsetBool(bytes32 _key, bool _value) internal{ rocketStorage.setBool(_key, _value); }
functionsetInt(bytes32 _key, int _value) internal{ rocketStorage.setInt(_key, _value); }
functionsetBytes32(bytes32 _key, bytes32 _value) internal{ rocketStorage.setBytes32(_key, _value); }
/// @dev Storage delete methodsfunctiondeleteAddress(bytes32 _key) internal{ rocketStorage.deleteAddress(_key); }
functiondeleteUint(bytes32 _key) internal{ rocketStorage.deleteUint(_key); }
functiondeleteString(bytes32 _key) internal{ rocketStorage.deleteString(_key); }
functiondeleteBytes(bytes32 _key) internal{ rocketStorage.deleteBytes(_key); }
functiondeleteBool(bytes32 _key) internal{ rocketStorage.deleteBool(_key); }
functiondeleteInt(bytes32 _key) internal{ rocketStorage.deleteInt(_key); }
functiondeleteBytes32(bytes32 _key) internal{ rocketStorage.deleteBytes32(_key); }
/// @dev Storage arithmetic methodsfunctionaddUint(bytes32 _key, uint256 _amount) internal{ rocketStorage.addUint(_key, _amount); }
functionsubUint(bytes32 _key, uint256 _amount) internal{ rocketStorage.subUint(_key, _amount); }
}
Contract Source Code
File 10 of 22: RocketDAOProtocolSettingsMinipoolInterface.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/pragmasolidity >0.5.0 <0.9.0;// SPDX-License-Identifier: GPL-3.0-onlyimport"../../../../types/MinipoolDeposit.sol";
interfaceRocketDAOProtocolSettingsMinipoolInterface{
functiongetLaunchBalance() externalviewreturns (uint256);
functiongetPreLaunchValue() externalpurereturns (uint256);
functiongetDepositUserAmount(MinipoolDeposit _depositType) externalviewreturns (uint256);
functiongetFullDepositUserAmount() externalviewreturns (uint256);
functiongetHalfDepositUserAmount() externalviewreturns (uint256);
functiongetVariableDepositAmount() externalviewreturns (uint256);
functiongetSubmitWithdrawableEnabled() externalviewreturns (bool);
functiongetBondReductionEnabled() externalviewreturns (bool);
functiongetLaunchTimeout() externalviewreturns (uint256);
functiongetMaximumCount() externalviewreturns (uint256);
functionisWithinUserDistributeWindow(uint256 _time) externalviewreturns (bool);
functionhasUserDistributeWindowPassed(uint256 _time) externalviewreturns (bool);
functiongetUserDistributeWindowStart() externalviewreturns (uint256);
functiongetUserDistributeWindowLength() externalviewreturns (uint256);
}
Contract Source Code
File 11 of 22: RocketDAOProtocolSettingsNodeInterface.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/pragmasolidity >0.5.0 <0.9.0;// SPDX-License-Identifier: GPL-3.0-onlyinterfaceRocketDAOProtocolSettingsNodeInterface{
functiongetRegistrationEnabled() externalviewreturns (bool);
functiongetSmoothingPoolRegistrationEnabled() externalviewreturns (bool);
functiongetDepositEnabled() externalviewreturns (bool);
functiongetVacantMinipoolsEnabled() externalviewreturns (bool);
functiongetMinimumPerMinipoolStake() externalviewreturns (uint256);
functiongetMaximumPerMinipoolStake() externalviewreturns (uint256);
functiongetMaximumStakeForVotingPower() externalviewreturns (uint256);
}
Contract Source Code
File 12 of 22: RocketDAOProtocolSettingsRewardsInterface.sol
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/// SPDX-License-Identifier: GPL-3.0-onlypragmasolidity >0.5.0 <0.9.0;interfaceRocketDAOProtocolSettingsRewardsInterface{
functionsetSettingRewardsClaimers(uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent) external;
functiongetRewardsClaimerPerc(stringmemory _contractName) externalviewreturns (uint256);
functiongetRewardsClaimersPerc() externalviewreturns (uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent);
functiongetRewardsClaimersTrustedNodePerc() externalviewreturns (uint256);
functiongetRewardsClaimersProtocolPerc() externalviewreturns (uint256);
functiongetRewardsClaimersNodePerc() externalviewreturns (uint256);
functiongetRewardsClaimersTimeUpdated() externalviewreturns (uint256);
functiongetRewardsClaimIntervalPeriods() externalviewreturns (uint256);
functiongetRewardsClaimIntervalTime() externalviewreturns (uint256);
}
/**
* .
* / \
* |.'.|
* |'.'|
* ,'| |'.
* |,-'-|-'-.|
* __|_| | _ _ _____ _
* | ___ \| | | | | | ___ \ | |
* | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | |
* | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| |
* | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | |
* \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|
* +---------------------------------------------------+
* | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM |
* +---------------------------------------------------+
*
* Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to
* be community-owned, decentralised, permissionless, & trustless.
*
* For more information about Rocket Pool, visit https://rocketpool.net
*
* Authored by the Rocket Pool Core Team
* Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors
* A special thanks to the Rocket Pool community for all their contributions.
*
*/// SPDX-License-Identifier: GPL-3.0-onlypragmasolidity 0.8.18;import"../../interface/util/IERC20.sol";
import"../RocketBase.sol";
import"../../interface/minipool/RocketMinipoolManagerInterface.sol";
import"../../interface/network/RocketNetworkPricesInterface.sol";
import"../../interface/node/RocketNodeStakingInterface.sol";
import"../../interface/dao/protocol/settings/RocketDAOProtocolSettingsRewardsInterface.sol";
import"../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
import"../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
import"../../interface/RocketVaultInterface.sol";
import"../../interface/util/AddressSetStorageInterface.sol";
import"../../interface/network/RocketNetworkSnapshotsInterface.sol";
import"../network/RocketNetworkSnapshots.sol";
import"../../interface/node/RocketNodeManagerInterface.sol";
/// @notice Handles node deposits and minipool creationcontractRocketNodeStakingisRocketBase, RocketNodeStakingInterface{
// Constantsbytes32immutableinternal totalKey;
// EventseventRPLStaked(addressindexedfrom, uint256 amount, uint256 time);
eventRPLWithdrawn(addressindexed to, uint256 amount, uint256 time);
eventRPLSlashed(addressindexed node, uint256 amount, uint256 ethValue, uint256 time);
eventStakeRPLForAllowed(addressindexed node, addressindexed caller, bool allowed, uint256 time);
eventRPLLockingAllowed(addressindexed node, bool allowed, uint256 time);
eventRPLLocked(addressindexedfrom, uint256 amount, uint256 time);
eventRPLUnlocked(addressindexedfrom, uint256 amount, uint256 time);
eventRPLTransferred(addressindexedfrom, addressindexed to, uint256 amount, uint256 time);
modifieronlyRPLWithdrawalAddressOrNode(address _nodeAddress) {
// Check that the call is coming from RPL withdrawal address (or node if unset)
RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
require(msg.sender== rplWithdrawalAddress, "Must be called from RPL withdrawal address");
} else {
require(msg.sender== _nodeAddress, "Must be called from node address");
}
_;
}
constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
version =5;
// Precompute keys
totalKey =keccak256(abi.encodePacked("rpl.staked.total.amount"));
}
/// @notice Returns the total quantity of RPL staked on the networkfunctiongetTotalRPLStake() overrideexternalviewreturns (uint256) {
return getUint(totalKey);
}
/// @dev Increases the total network RPL stake/// @param _amount How much to increase byfunctionincreaseTotalRPLStake(uint256 _amount) private{
addUint(totalKey, _amount);
}
/// @dev Decrease the total network RPL stake/// @param _amount How much to decrease byfunctiondecreaseTotalRPLStake(uint256 _amount) private{
subUint(totalKey, _amount);
}
/// @notice Returns the amount a given node operator has staked/// @param _nodeAddress The address of the node operator to queryfunctiongetNodeRPLStake(address _nodeAddress) overridepublicviewreturns (uint256) {
bytes32 key =keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
(bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
uint256 stake =uint256(value);
if (!exists){
// Fallback to old value
stake = getUint(key);
}
return stake;
}
/// @dev Increases a node operator's RPL stake/// @param _amount How much to increase byfunctionincreaseNodeRPLStake(address _nodeAddress, uint256 _amount) private{
RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
bytes32 key =keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
(bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
if (!exists){
value =uint224(getUint(key));
}
rocketNetworkSnapshots.push(key, value +uint224(_amount));
}
/// @dev Decrease a node operator's RPL stake/// @param _amount How much to decrease byfunctiondecreaseNodeRPLStake(address _nodeAddress, uint256 _amount) private{
RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
bytes32 key =keccak256(abi.encodePacked("rpl.staked.node.amount", _nodeAddress));
(bool exists,, uint224 value) = rocketNetworkSnapshots.latest(key);
if (!exists){
value =uint224(getUint(key));
}
rocketNetworkSnapshots.push(key, value -uint224(_amount));
}
/// @notice Returns a node's matched ETH amount (amount taken from protocol to stake)/// @param _nodeAddress The address of the node operator to queryfunctiongetNodeETHMatched(address _nodeAddress) overridepublicviewreturns (uint256) {
RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
bytes32 key =keccak256(abi.encodePacked("eth.matched.node.amount", _nodeAddress));
(bool exists, , uint224 value) = rocketNetworkSnapshots.latest(key);
if (exists) {
// Value was previously set in a snapshot so return thatreturn value;
} else {
// Fallback to old methoduint256 ethMatched = getUint(key);
if (ethMatched >0) {
// Value was previously calculated and stored so return thatreturn ethMatched;
} else {
// Fallback for backwards compatibility before ETH matched was recorded (all legacy minipools matched 16 ETH from protocol)
RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
return rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress) *16ether;
}
}
}
/// @notice Returns a node's provided ETH amount (amount supplied to create minipools)/// @param _nodeAddress The address of the node operator to queryfunctiongetNodeETHProvided(address _nodeAddress) overridepublicviewreturns (uint256) {
// Get contracts
RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
uint256 activeMinipoolCount = rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress);
// Retrieve stored ETH matched valueuint256 ethMatched = getNodeETHMatched(_nodeAddress);
if (ethMatched >0) {
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
// ETH provided is number of staking minipools * 32 - eth matcheduint256 totalEthStaked = activeMinipoolCount * launchAmount;
return totalEthStaked - ethMatched;
} else {
// Fallback for legacy minipools is number of staking minipools * 16return activeMinipoolCount *16ether;
}
}
/// @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 queryfunctiongetNodeETHCollateralisationRatio(address _nodeAddress) overridepublicviewreturns (uint256) {
uint256 ethMatched = getNodeETHMatched(_nodeAddress);
if (ethMatched ==0) {
// Node operator only has legacy minipools and all legacy minipools had a 1:1 ratioreturn calcBase *2;
} else {
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance();
RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
uint256 totalEthStaked = rocketMinipoolManager.getNodeActiveMinipoolCount(_nodeAddress) * launchAmount;
return (totalEthStaked * calcBase) / (totalEthStaked - ethMatched);
}
}
/// @notice Returns the timestamp at which a node last staked RPLfunctiongetNodeRPLStakedTime(address _nodeAddress) overridepublicviewreturns (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 setfunctionsetNodeRPLStakedTime(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 forfunctiongetNodeEffectiveRPLStake(address _nodeAddress) overridepublicviewreturns (uint256) {
// Load contracts
RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
// Get node's current RPL stakeuint256 rplStake = getNodeRPLStake(_nodeAddress);
// Retrieve variables for calculationsuint256 matchedETH = getNodeETHMatched(_nodeAddress);
uint256 providedETH = getNodeETHProvided(_nodeAddress);
uint256 rplPrice = rocketNetworkPrices.getRPLPrice();
// RPL stake cannot exceed maximumuint256 maximumStakePercent = rocketDAOProtocolSettingsNode.getMaximumPerMinipoolStake();
uint256 maximumStake = providedETH * maximumStakePercent / rplPrice;
if (rplStake > maximumStake) {
return maximumStake;
}
// If RPL stake is lower than minimum, node has no effective stakeuint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
uint256 minimumStake = matchedETH * minimumStakePercent / rplPrice;
if (rplStake < minimumStake) {
return0;
}
// Otherwise, return the actual stakereturn 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 forfunctiongetNodeMinimumRPLStake(address _nodeAddress) overrideexternalviewreturns (uint256) {
// Load contracts
RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
// Retrieve variablesuint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
uint256 matchedETH = getNodeETHMatched(_nodeAddress);
return matchedETH * minimumStakePercent / rocketNetworkPrices.getRPLPrice();
}
/// @notice Calculate and return a node's maximum RPL stake to fully collateralise their minipools/// @param _nodeAddress The address of the node operator to calculate forfunctiongetNodeMaximumRPLStake(address _nodeAddress) overridepublicviewreturns (uint256) {
// Load contracts
RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
// Retrieve variablesuint256 maximumStakePercent = rocketDAOProtocolSettingsNode.getMaximumPerMinipoolStake();
uint256 providedETH = getNodeETHProvided(_nodeAddress);
return providedETH * maximumStakePercent / rocketNetworkPrices.getRPLPrice();
}
/// @notice Calculate and return a node's limit of how much user ETH they can use based on RPL stake/// @param _nodeAddress The address of the node operator to calculate forfunctiongetNodeETHMatchedLimit(address _nodeAddress) overrideexternalviewreturns (uint256) {
// Load contracts
RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
// Calculate & return limituint256 minimumStakePercent = rocketDAOProtocolSettingsNode.getMinimumPerMinipoolStake();
return getNodeRPLStake(_nodeAddress) *rocketNetworkPrices.getRPLPrice() / minimumStakePercent;
}
/// @notice Returns whether this node allows RPL locking or not/// @param _nodeAddress The address of the node operator to query forfunctiongetRPLLockingAllowed(address _nodeAddress) externalviewreturns (bool) {
return getBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress)));
}
/// @notice Accept an RPL stake from the node operator's own address/// Requires the node's RPL withdrawal address to be unset/// @param _amount The amount of RPL to stakefunctionstakeRPL(uint256 _amount) overrideexternal{
stakeRPLFor(msg.sender, _amount);
}
/// @notice Accept an RPL stake from any address for a specified node/// Requires caller to have approved this contract to spend RPL/// Requires caller to be on the node operator's allow list (see `setStakeForAllowed`)/// @param _nodeAddress The address of the node operator to stake on behalf of/// @param _amount The amount of RPL to stakefunctionstakeRPLFor(address _nodeAddress, uint256 _amount) overridepubliconlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredNode(_nodeAddress) {
// Must be node's RPL withdrawal address if set or the node's address or an allow listed address or rocketMerkleDistributorMainnetif (msg.sender!= getAddress(keccak256(abi.encodePacked("contract.address", "rocketMerkleDistributorMainnet")))) {
RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
bool fromNode =false;
if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
fromNode =msg.sender== rplWithdrawalAddress;
} else {
address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
fromNode = (msg.sender== _nodeAddress) || (msg.sender== withdrawalAddress);
}
if (!fromNode) {
require(getBool(keccak256(abi.encodePacked("node.stake.for.allowed", _nodeAddress, msg.sender))), "Not allowed to stake for");
}
}
_stakeRPL(_nodeAddress, _amount);
}
/// @notice Sets the allow state for this node to perform functions that require locking RPL/// @param _nodeAddress The address of the node operator to change the state for/// @param _allowed Whether locking is allowed or notfunctionsetRPLLockingAllowed(address _nodeAddress, bool _allowed) overrideexternalonlyLatestContract("rocketNodeStaking", address(this)) onlyRPLWithdrawalAddressOrNode(_nodeAddress) {
// Set the value
setBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress)), _allowed);
// Log itemit RPLLockingAllowed(_nodeAddress, _allowed, block.timestamp);
}
/// @notice Explicitly allow or remove allowance of an address to be able to stake on behalf of a node/// @dev The node operator is determined by the address calling this method, it is here for backwards compatibility/// @param _caller The address you wish to allow/// @param _allowed Whether the address is allowed or deniedfunctionsetStakeRPLForAllowed(address _caller, bool _allowed) overrideexternal{
setStakeRPLForAllowed(msg.sender, _caller, _allowed);
}
/// @notice Explicitly allow or remove allowance of an address to be able to stake on behalf of a node/// @param _nodeAddress The address of the node operator allowing the caller/// @param _caller The address you wish to allow/// @param _allowed Whether the address is allowed or deniedfunctionsetStakeRPLForAllowed(address _nodeAddress, address _caller, bool _allowed) overridepubliconlyLatestContract("rocketNodeStaking", address(this)) onlyRPLWithdrawalAddressOrNode(_nodeAddress) {
// Set the value
setBool(keccak256(abi.encodePacked("node.stake.for.allowed", _nodeAddress, _caller)), _allowed);
// Log itemit StakeRPLForAllowed(_nodeAddress, _caller, _allowed, block.timestamp);
}
/// @dev Internal logic for staking RPL/// @param _nodeAddress The address to increase the RPL stake of/// @param _amount The amount of RPL to stakefunction_stakeRPL(address _nodeAddress, uint256 _amount) internal{
// Load contractsaddress rplTokenAddress = getContractAddress("rocketTokenRPL");
address rocketVaultAddress = getContractAddress("rocketVault");
IERC20 rplToken = IERC20(rplTokenAddress);
RocketVaultInterface rocketVault = RocketVaultInterface(rocketVaultAddress);
// Transfer RPL tokensrequire(rplToken.transferFrom(msg.sender, address(this), _amount), "Could not transfer RPL to staking contract");
// Deposit RPL tokens to vaultrequire(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 eventemit RPLStaked(_nodeAddress, _amount, block.timestamp);
}
/// @notice Returns the amount of RPL that is locked for a given node/// @param _nodeAddress The address of the node operator to query forfunctiongetNodeRPLLocked(address _nodeAddress) overridepublicviewreturns (uint256) {
return getUint(keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress)));
}
/// @notice Locks an amount of RPL from being withdrawn even if the node operator is over capitalised/// @param _nodeAddress The address of the node operator/// @param _amount The amount of RPL to lockfunctionlockRPL(address _nodeAddress, uint256 _amount) overrideexternalonlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() {
// Check statusrequire(getBool(keccak256(abi.encodePacked("rpl.locking.allowed", _nodeAddress))), "Node is not allowed to lock RPL");
// The node must have unlocked stake equaling or greater than the amountuint256 rplStake = getNodeRPLStake(_nodeAddress);
bytes32 lockedStakeKey =keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress));
uint256 lockedStake = getUint(lockedStakeKey);
require(rplStake - lockedStake >= _amount, "Not enough staked RPL");
// Increase locked RPL
setUint(lockedStakeKey, lockedStake + _amount);
// Emit eventemit RPLLocked(_nodeAddress, _amount, block.timestamp);
}
/// @notice Unlocks an amount of RPL making it possible to withdraw if the nod is over capitalised/// @param _nodeAddress The address of the node operator/// @param _amount The amount of RPL to unlockfunctionunlockRPL(address _nodeAddress, uint256 _amount) overrideexternalonlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() {
// The node must have locked stake equaling or greater than the amountbytes32 lockedStakeKey =keccak256(abi.encodePacked("rpl.locked.node.amount", _nodeAddress));
uint256 lockedStake = getUint(lockedStakeKey);
require(_amount <= lockedStake, "Not enough locked RPL");
// Decrease locked RPL
setUint(lockedStakeKey, lockedStake - _amount);
// Emit eventemit RPLUnlocked(_nodeAddress, _amount, block.timestamp);
}
/// @notice Transfers RPL from one node to another/// @param _from The node to transfer from/// @param _to The node to transfer to/// @param _amount The amount of RPL to transferfunctiontransferRPL(address _from, address _to, uint256 _amount) overrideexternalonlyLatestContract("rocketNodeStaking", address(this)) onlyLatestNetworkContract() onlyRegisteredNode(_from) {
// Check sender has enough RPLrequire(getNodeRPLStake(_from) >= _amount, "Sender has insufficient RPL");
// Transfer the stake
decreaseNodeRPLStake(_from, _amount);
increaseNodeRPLStake(_to, _amount);
// Emit eventemit RPLTransferred(_from, _to, _amount, block.timestamp);
}
/// @notice Withdraw staked RPL back to the node account or withdraw RPL address/// Can only be called by a node if they have not set their RPL withdrawal address/// @param _amount The amount of RPL to withdrawfunctionwithdrawRPL(uint256 _amount) overrideexternal{
withdrawRPL(msg.sender, _amount);
}
/// @notice Withdraw staked RPL back to the node account or withdraw RPL address/// If RPL withdrawal address has been set, must be called from it. Otherwise, must be called from/// node's primary withdrawal address or their node address./// @param _nodeAddress The address of the node withdrawing/// @param _amount The amount of RPL to withdrawfunctionwithdrawRPL(address _nodeAddress, uint256 _amount) overridepubliconlyLatestContract("rocketNodeStaking", address(this)) {
// Check valid noderequire(getBool(keccak256(abi.encodePacked("node.exists", _nodeAddress))), "Invalid node");
// Check address is permitted to withdraw
RocketNodeManagerInterface rocketNodeManager = RocketNodeManagerInterface(getContractAddress("rocketNodeManager"));
address rplWithdrawalAddress = rocketNodeManager.getNodeRPLWithdrawalAddress(_nodeAddress);
if (rocketNodeManager.getNodeRPLWithdrawalAddressIsSet(_nodeAddress)) {
// If RPL withdrawal address is set, must be called from itrequire(msg.sender== rplWithdrawalAddress, "Invalid caller");
} else {
// Otherwise, must be called from node address or withdrawal addressaddress withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
require(msg.sender== _nodeAddress ||msg.sender== withdrawalAddress, "Invalid caller");
}
// Load contracts
RocketDAOProtocolSettingsRewardsInterface rocketDAOProtocolSettingsRewards = RocketDAOProtocolSettingsRewardsInterface(getContractAddress("rocketDAOProtocolSettingsRewards"));
RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
// Check cooldown period (one claim period) has passed since RPL last stakedrequire(block.timestamp- getNodeRPLStakedTime(_nodeAddress) >= rocketDAOProtocolSettingsRewards.getRewardsClaimIntervalTime(), "The withdrawal cooldown period has not passed");
// Get & check node's current RPL stakeuint256 rplStake = getNodeRPLStake(_nodeAddress);
uint256 lockedStake = getNodeRPLLocked(_nodeAddress);
require(rplStake - lockedStake >= _amount, "Withdrawal amount exceeds node's staked RPL balance");
// Check withdrawal would not under collateralise noderequire(rplStake - _amount >= getNodeMaximumRPLStake(_nodeAddress) + lockedStake, "Node's staked RPL balance after withdrawal is less than required balance");
// Update RPL stake amounts
decreaseTotalRPLStake(_amount);
decreaseNodeRPLStake(_nodeAddress, _amount);
// Transfer RPL tokens to node's RPL withdrawal address (if unset, defaults to primary withdrawal address)
rocketVault.withdrawToken(rplWithdrawalAddress, IERC20(getContractAddress("rocketTokenRPL")), _amount);
// Emit RPL withdrawn eventemit RPLWithdrawn(_nodeAddress, _amount, block.timestamp);
}
/// @notice Slash a node's RPL by an ETH amount/// Only accepts calls from registered minipools/// @param _nodeAddress The address to slash RPL from/// @param _ethSlashAmount The amount of RPL to slash denominated in ETH valuefunctionslashRPL(address _nodeAddress, uint256 _ethSlashAmount) overrideexternalonlyLatestContract("rocketNodeStaking", address(this)) onlyRegisteredMinipool(msg.sender) {
// Load contracts
RocketNetworkPricesInterface rocketNetworkPrices = RocketNetworkPricesInterface(getContractAddress("rocketNetworkPrices"));
RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
// Calculate RPL amount to slashuint256 rplSlashAmount = calcBase * _ethSlashAmount / rocketNetworkPrices.getRPLPrice();
// Cap slashed amount to node's RPL stakeuint256 rplStake = getNodeRPLStake(_nodeAddress);
if (rplSlashAmount > rplStake) { rplSlashAmount = rplStake; }
// Transfer slashed amount to auction contractif(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 eventemit RPLSlashed(_nodeAddress, rplSlashAmount, _ethSlashAmount, block.timestamp);
}
}