Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use eth in queue #255

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ contract RocketDAOProtocolSettingsDeposit is RocketDAOProtocolSettings, RocketDA
setSettingBool("deposit.assign.enabled", true);
setSettingUint("deposit.minimum", 0.01 ether);
setSettingUint("deposit.pool.maximum", 160 ether);
setSettingUint("deposit.assign.maximum", 2);
setSettingUint("deposit.assign.maximum", 90);
setSettingUint("deposit.assign.socializedmaximum", 2);
setSettingUint("deposit.fee", 0.0005 ether); // Set to approx. 1 day of rewards at 18.25% APR
// Settings initialised
setBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")), true);
Expand Down Expand Up @@ -52,6 +53,11 @@ contract RocketDAOProtocolSettingsDeposit is RocketDAOProtocolSettings, RocketDA
return getSettingUint("deposit.assign.maximum");
}

// The maximum number of socialized (ie, not related to deposit size) assignments to perform
function getMaximumDepositSocializedAssignments() override external view returns (uint256) {
return getSettingUint("deposit.assign.socializedmaximum");
}

// Get the fee paid on deposits
function getDepositFee() override external view returns (uint256) {
return getSettingUint("deposit.fee");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "./RocketDAOProtocolSettings.sol";
import "../../../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
import "../../../../interface/dao/node/settings/RocketDAONodeTrustedSettingsMinipoolInterface.sol";
import "../../../../types/MinipoolDeposit.sol";
import "../../../../contracts/contract/minipool/RocketMinipoolDelegate.sol";

// Network minipool settings
contract RocketDAOProtocolSettingsMinipool is RocketDAOProtocolSettings, RocketDAOProtocolSettingsMinipoolInterface {
Expand Down Expand Up @@ -49,29 +50,25 @@ contract RocketDAOProtocolSettingsMinipool is RocketDAOProtocolSettings, RocketD
}

// Required node deposit amounts
function getDepositNodeAmount(MinipoolDeposit _depositType) override external pure returns (uint256) {
if (_depositType == MinipoolDeposit.Full) { return getFullDepositNodeAmount(); }
if (_depositType == MinipoolDeposit.Half) { return getHalfDepositNodeAmount(); }
if (_depositType == MinipoolDeposit.Empty) { return getEmptyDepositNodeAmount(); }
return 0;
}
function getFullDepositNodeAmount() override public pure returns (uint256) {
return getLaunchBalance();
}
function getHalfDepositNodeAmount() override public pure returns (uint256) {
return getLaunchBalance().div(2);
}
function getEmptyDepositNodeAmount() override public pure returns (uint256) {
return 0 ether;
}

// Required user deposit amounts
function getDepositUserAmount(MinipoolDeposit _depositType) override external pure returns (uint256) {
if (_depositType == MinipoolDeposit.Efficient) { return getEfficientDepositUserAmount(); }
if (_depositType == MinipoolDeposit.Full) { return getFullDepositUserAmount(); }
if (_depositType == MinipoolDeposit.Half) { return getHalfDepositUserAmount(); }
if (_depositType == MinipoolDeposit.Empty) { return getEmptyDepositUserAmount(); }
return 0;
}
function getEfficientDepositUserAmount() override public pure returns (uint256) {
address delegateAddress = getContractAddress("rocketMinipoolDelegate");
return getLaunchBalance().sub(delegateAddress.efficientprelaunchAmount);
}
function getFullDepositUserAmount() override public pure returns (uint256) {
return getLaunchBalance().div(2);
}
Expand Down
83 changes: 70 additions & 13 deletions contracts/contract/deposit/RocketDepositPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,18 @@ contract RocketDepositPool is RocketBase, RocketDepositPoolInterface, RocketVaul
require(rocketDAOProtocolSettingsDeposit.getDepositEnabled(), "Deposits into Rocket Pool are currently disabled");
require(msg.value >= rocketDAOProtocolSettingsDeposit.getMinimumDeposit(), "The deposited amount is less than the minimum deposit size");
RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
require(rocketVault.balanceOf("rocketDepositPool").add(msg.value) <= rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize(), "The deposit pool size after depositing exceeds the maximum size");
uint256 capacityNeeded = rocketVault.balanceOf("rocketDepositPool").add(msg.value);
if (capacityNeeded > rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize()) {
// Doing a conditional require() instead of a single one optimizes for the common
// case where capacityNeeded fits in the deposit pool without looking at the queue
if (rocketDAOProtocolSettingsDeposit.getAssignDepositsEnabled()) {
RocketMinipoolQueueInterface rocketMinipoolQueue = RocketMinipoolQueueInterface(getContractAddress("rocketMinipoolQueue"));
require(capacityNeeded <= rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize() + rocketMinipoolQueue.getEffectiveCapacity(),
"The deposit pool size after depositing (and matching with minipools) exceeds the maximum size");
} else {
revert("The deposit pool size after depositing exceeds the maximum size");
}
}
// Calculate deposit fee
uint256 depositFee = msg.value.mul(rocketDAOProtocolSettingsDeposit.getDepositFee()).div(calcBase);
uint256 depositNet = msg.value.sub(depositFee);
Expand Down Expand Up @@ -140,6 +151,7 @@ contract RocketDepositPool is RocketBase, RocketDepositPoolInterface, RocketVaul
}

// Assigns deposits to available minipools, returns false if assignment is currently disabled
// Can assign deposits up to the value of the deposit plus getMaximumDepositAssignments()
function _assignDeposits(RocketVaultInterface _rocketVault, RocketDAOProtocolSettingsDepositInterface _rocketDAOProtocolSettingsDeposit) private returns (bool) {
// Check if assigning deposits is enabled
if (!_rocketDAOProtocolSettingsDeposit.getAssignDepositsEnabled()) {
Expand All @@ -151,29 +163,74 @@ contract RocketDepositPool is RocketBase, RocketDepositPoolInterface, RocketVaul
// Setup initial variable values
uint256 balance = _rocketVault.balanceOf("rocketDepositPool");
uint256 totalEther = 0;

// Calculate minipool assignments
uint256 i;
uint256 assignmentIndex = 0;
uint256 minipoolCapacity = 0;
uint256 depositValueForAssignments = msg.value;
uint256 socializedAssignmentsLeft = _rocketDAOProtocolSettingsDeposit.getMaximumDepositSocializedAssignments();
uint256 maxAssignments = _rocketDAOProtocolSettingsDeposit.getMaximumDepositAssignments();
MinipoolAssignment[] memory assignments = new MinipoolAssignment[](maxAssignments);
MinipoolDeposit depositType = MinipoolDeposit.None;
uint256 count = 0;
uint256 minipoolCapacity = 0;
for (uint256 i = 0; i < maxAssignments; ++i) {
// Optimised for multiple of the same deposit type
if (count == 0) {
(depositType, count) = rocketMinipoolQueue.getNextDeposit();
if (depositType == MinipoolDeposit.None) { break; }
minipoolCapacity = rocketDAOProtocolSettingsMinipool.getDepositUserAmount(depositType);

// Prepare half deposit assignments
minipoolCapacity = rocketDAOProtocolSettingsMinipool.getDepositUserAmount(MinipoolDeposit.Half);
for (i=0; i < rocketMinipoolQueue.getLength(MinipoolDeposit.Half); ++i) {
if (assignmentIndex == maxAssignments) { break; }
if (depositValueForAssignments < minipoolCapacity) {
if (socializedAssignmentsLeft == 0) { break; }
else {socializedAssignmentsLeft--;}
} else {
depositValueForAssignments.sub(minipoolCapacity);
}
count--;
if (minipoolCapacity == 0 || balance.sub(totalEther) < minipoolCapacity) { break; }
if (balance.sub(totalEther) < minipoolCapacity) { break; }
// Dequeue the minipool
address minipoolAddress = rocketMinipoolQueue.dequeueMinipoolByDeposit(depositType);
address minipoolAddress = rocketMinipoolQueue.dequeueMinipoolByDeposit(MinipoolDeposit.Half);
// Update running total
totalEther = totalEther.add(minipoolCapacity);
// Add assignment, increment index
assignments[assignmentIndex].etherAssigned = minipoolCapacity;
assignments[assignmentIndex].minipoolAddress = minipoolAddress;
assignmentIndex++;
}

// Prepare full deposit assignments
minipoolCapacity = rocketDAOProtocolSettingsMinipool.getDepositUserAmount(MinipoolDeposit.Full);
for (i=0; i < rocketMinipoolQueue.getLength(MinipoolDeposit.Full); ++i) {
if (assignmentIndex == maxAssignments) { break; }
if (depositValueForAssignments < minipoolCapacity) {
if (socializedAssignmentsLeft == 0) { break; }
else {socializedAssignmentsLeft--;}
}
if (balance.sub(totalEther) < minipoolCapacity) { break; }
// Dequeue the minipool
address minipoolAddress = rocketMinipoolQueue.dequeueMinipoolByDeposit(MinipoolDeposit.Full);
// Update running total
totalEther = totalEther.add(minipoolCapacity);
// Add assignment, increment index
assignments[assignmentIndex].etherAssigned = minipoolCapacity;
assignments[assignmentIndex].minipoolAddress = minipoolAddress;
assignmentIndex++;
}

// Prepare efficient deposit assignments - will always need 31 ETH
count = rocketMinipoolQueue.getLength(MinipoolDeposit.Efficient);
minipoolCapacity = rocketDAOProtocolSettingsMinipool.getDepositUserAmount(MinipoolDeposit.Efficient);
for (i; i < i + count; ++i) { // NOTE - this is a weird line - we continue the indexing from the full deposit loop
if (depositValueForAssignments < minipoolCapacity) {
if (socializedAssignments == 0) { break; }
else {socializedAssignments--;}
}
if (balance.sub(totalEther) < minipoolCapacity) { break; }
// Dequeue the minipool
address minipoolAddress = rocketMinipoolQueue.dequeueMinipoolByDeposit(MinipoolDeposit.Efficient);
// Update running total
totalEther = totalEther.add(minipoolCapacity);
// Add assignment
assignments[i].etherAssigned = minipoolCapacity;
assignments[i].minipoolAddress = minipoolAddress;
}

if (totalEther > 0) {
// Withdraw ETH from vault
_rocketVault.withdrawEther(totalEther);
Expand Down
53 changes: 34 additions & 19 deletions contracts/contract/minipool/RocketMinipoolDelegate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity 0.7.6;
import "@openzeppelin/contracts/math/SafeMath.sol";

import "./RocketMinipoolStorageLayout.sol";
import "../../interface/RocketVaultInterface.sol";
import "../../interface/casper/DepositInterface.sol";
import "../../interface/deposit/RocketDepositPoolInterface.sol";
import "../../interface/minipool/RocketMinipoolInterface.sol";
Expand All @@ -17,6 +18,7 @@ import "../../interface/node/RocketNodeStakingInterface.sol";
import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
import "../../interface/dao/node/settings/RocketDAONodeTrustedSettingsMinipoolInterface.sol";
import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsDepositInterface.sol";
import "../../interface/dao/node/RocketDAONodeTrustedInterface.sol";
import "../../interface/network/RocketNetworkFeesInterface.sol";
import "../../interface/token/RocketTokenRETHInterface.sol";
Expand All @@ -31,6 +33,7 @@ contract RocketMinipoolDelegate is RocketMinipoolStorageLayout, RocketMinipoolIn
uint8 public constant version = 2; // Used to identify which delegate contract each minipool is using
uint256 constant calcBase = 1 ether;
uint256 constant prelaunchAmount = 16 ether; // The amount of ETH initially deposited when minipool is created
uint256 constant efficientprelaunchAmount = 1 ether; // The amount of ETH initially deposited when minipool is created
uint256 constant distributionCooldown = 100; // Number of blocks that must pass between calls to distributeBalance

// Libs
Expand Down Expand Up @@ -131,16 +134,20 @@ contract RocketMinipoolDelegate is RocketMinipoolStorageLayout, RocketMinipoolIn
function nodeDeposit(bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot) override external payable onlyLatestContract("rocketNodeDeposit", msg.sender) onlyInitialised {
// Check current status & node deposit status
require(status == MinipoolStatus.Initialised, "The node deposit can only be assigned while initialised");
require(!nodeDepositAssigned, "The node deposit has already been assigned");
// Progress full minipool to prelaunch
if (depositType == MinipoolDeposit.Full) { setStatus(MinipoolStatus.Prelaunch); }
// Update node deposit details
nodeDepositBalance = msg.value;
nodeDepositAssigned = true;
require(nodeDepositBalance == 0, "The minipool already has a previous nodeDeposit");

// Emit ether deposited event
emit EtherDeposited(msg.sender, msg.value, block.timestamp);
// Perform the pre-stake to lock in withdrawal credentials on beacon chain
preStake(_validatorPubkey, _validatorSignature, _depositDataRoot);

nodeDepositBalance = msg.value;
nodeDepositAssigned = 0; // should be 0 from initialization anyhow

// Deposit ETH (except the ETH needed to preStake) without minting rETH
// Transfer to vault directly instead of via processDeposits to avoid assigning twice
RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
rocketVault.depositEther{value: msg.value.sub(efficientprelaunchAmount)}();
}

// Assign user deposited ETH to the minipool and mark it as prelaunch
Expand All @@ -151,15 +158,19 @@ contract RocketMinipoolDelegate is RocketMinipoolStorageLayout, RocketMinipoolIn
require(userDepositAssignedTime == 0, "The user deposit has already been assigned");
// Progress initialised minipool to prelaunch
if (status == MinipoolStatus.Initialised) { setStatus(MinipoolStatus.Prelaunch); }
// Update user deposit details
userDepositBalance = msg.value;
userDepositAssignedTime = block.timestamp;
// Refinance full minipool

if (depositType == MinipoolDeposit.Full) {
// Update node balances
// Refinance full minipool
nodeDepositBalance = nodeDepositBalance.sub(msg.value);
nodeRefundBalance = nodeRefundBalance.add(msg.value);
}


nodeDepositAssigned = true; // indicate that the node deposit was returned for Efficient queue
// Update user deposit details
userDepositBalance = msg.value;
userDepositAssignedTime = block.timestamp;

// Emit ether deposited event
emit EtherDeposited(msg.sender, msg.value, block.timestamp);
}
Expand Down Expand Up @@ -220,7 +231,11 @@ contract RocketMinipoolDelegate is RocketMinipoolStorageLayout, RocketMinipoolIn
DepositInterface casperDeposit = DepositInterface(getContractAddress("casperDeposit"));
RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
// Get launch amount
uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance().sub(prelaunchAmount);
if (depositType == MinipoolDeposit.Efficient) {
uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance().sub(efficientprelaunchAmount);
} else {
uint256 launchAmount = rocketDAOProtocolSettingsMinipool.getLaunchBalance().sub(prelaunchAmount);
}
// Check minipool balance
require(address(this).balance >= launchAmount, "Insufficient balance to begin staking");
// Retrieve validator pubkey from storage
Expand All @@ -237,17 +252,17 @@ contract RocketMinipoolDelegate is RocketMinipoolStorageLayout, RocketMinipoolIn
DepositInterface casperDeposit = DepositInterface(getContractAddress("casperDeposit"));
RocketMinipoolManagerInterface rocketMinipoolManager = RocketMinipoolManagerInterface(getContractAddress("rocketMinipoolManager"));
// Check minipool balance
require(address(this).balance >= prelaunchAmount, "Insufficient balance to pre-stake");
require(address(this).balance >= efficientprelaunchAmount, "Insufficient balance to pre-stake");
// Check validator pubkey is not in use
require(rocketMinipoolManager.getMinipoolByPubkey(_validatorPubkey) == address(0x0), "Validator pubkey is in use");
// Set minipool pubkey
rocketMinipoolManager.setMinipoolPubkey(_validatorPubkey);
// Get withdrawal credentials
bytes memory withdrawalCredentials = rocketMinipoolManager.getMinipoolWithdrawalCredentials(address(this));
// Send staking deposit to casper
casperDeposit.deposit{value : prelaunchAmount}(_validatorPubkey, withdrawalCredentials, _validatorSignature, _depositDataRoot);
casperDeposit.deposit{value : efficientprelaunchAmount}(_validatorPubkey, withdrawalCredentials, _validatorSignature, _depositDataRoot);
// Emit event
emit MinipoolPrestaked(_validatorPubkey, _validatorSignature, _depositDataRoot, prelaunchAmount, withdrawalCredentials, block.timestamp);
emit MinipoolPrestaked(_validatorPubkey, _validatorSignature, _depositDataRoot, efficientprelaunchAmount, withdrawalCredentials, block.timestamp);
}

// Mark the minipool as withdrawable
Expand Down Expand Up @@ -420,15 +435,15 @@ contract RocketMinipoolDelegate is RocketMinipoolStorageLayout, RocketMinipoolIn
}

// Dissolve the minipool, returning user deposited ETH to the deposit pool
// Only accepts calls from the minipool owner (node), or from any address if timed out
// Only accepts calls when in Prelaunch for too long without calling stake()
// In other words, this prevents User ETH from getting stuck when an NO fails to move forward
function dissolve() override external onlyInitialised {
// Check current status
require(status == MinipoolStatus.Initialised || status == MinipoolStatus.Prelaunch, "The minipool can only be dissolved while initialised or in prelaunch");
require(status == MinipoolStatus.Prelaunch, "The minipool can only be dissolved while in prelaunch");
// Load contracts
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
// Check if being dissolved by minipool owner or minipool is timed out
require(
(status == MinipoolStatus.Prelaunch && block.timestamp.sub(statusTime) >= rocketDAOProtocolSettingsMinipool.getLaunchTimeout()),
(block.timestamp.sub(statusTime) >= rocketDAOProtocolSettingsMinipool.getLaunchTimeout()),
"The minipool can only be dissolved once it has timed out"
);
// Perform the dissolution
Expand Down
Loading