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

Allows deposit size to scale with minipool queue space #251

Open
wants to merge 3 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);
// Settings initialised
setBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")), true);
}
Expand Down Expand Up @@ -51,4 +52,8 @@ 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");
}
}
72 changes: 56 additions & 16 deletions contracts/contract/deposit/RocketDepositPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,19 @@ 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");
}
}

// Record last deposit to time delay ability to withdraw
setUint(keccak256(abi.encodePacked("user.deposit.block", msg.sender)), block.number);
// Mint rETH to user account
Expand Down Expand Up @@ -138,6 +150,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 @@ -149,29 +162,56 @@ 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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems there's no way to handle running out of one deposit type and moving on to the next one?
We only getNextDeposit() on the first loop, so then we're stuck with that deposit type.
If we run out of that queue, I'm not really sure what happens when dequeueMinipoolByDeposit fails because there's nothing more in the queue (the require in that function fails, but I'm not sure where that ends up putting us cuz I'm a solidity newbie).

(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
assignments[i].etherAssigned = minipoolCapacity;
assignments[i].minipoolAddress = minipoolAddress;
// 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++;
}

if (totalEther > 0) {
// Withdraw ETH from vault
rocketVault.withdrawEther(totalEther);
Expand Down
36 changes: 2 additions & 34 deletions contracts/contract/minipool/RocketMinipoolQueue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,6 @@ contract RocketMinipoolQueue is RocketBase, RocketMinipoolQueueInterface {
return addressQueueStorage.getLength(_key);
}

// Get the total combined capacity of the queues
function getTotalCapacity() override external view returns (uint256) {
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
return (
getLength(queueKeyFull).mul(rocketDAOProtocolSettingsMinipool.getFullDepositUserAmount())
).add(
getLength(queueKeyHalf).mul(rocketDAOProtocolSettingsMinipool.getHalfDepositUserAmount())
).add(
getLength(queueKeyEmpty).mul(rocketDAOProtocolSettingsMinipool.getEmptyDepositUserAmount())
);
}

// Get the total effective capacity of the queues (used in node demand calculation)
function getEffectiveCapacity() override external view returns (uint256) {
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
Expand All @@ -79,28 +67,6 @@ contract RocketMinipoolQueue is RocketBase, RocketMinipoolQueueInterface {
);
}

// Get the capacity of the next available minipool
// Returns 0 if no minipools are available
function getNextCapacity() override external view returns (uint256) {
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
if (getLength(queueKeyHalf) > 0) { return rocketDAOProtocolSettingsMinipool.getHalfDepositUserAmount(); }
if (getLength(queueKeyFull) > 0) { return rocketDAOProtocolSettingsMinipool.getFullDepositUserAmount(); }
if (getLength(queueKeyEmpty) > 0) { return rocketDAOProtocolSettingsMinipool.getEmptyDepositUserAmount(); }
return 0;
}

// Get the deposit type of the next available minipool and the number of deposits in that queue
// Returns None if no minipools are available
function getNextDeposit() override external view returns (MinipoolDeposit, uint256) {
uint256 length = getLength(queueKeyHalf);
if (length > 0) { return (MinipoolDeposit.Half, length); }
length = getLength(queueKeyFull);
if (length > 0) { return (MinipoolDeposit.Full, length); }
length = getLength(queueKeyEmpty);
if (length > 0) { return (MinipoolDeposit.Empty, length); }
return (MinipoolDeposit.None, 0);
}

// Add a minipool to the end of the appropriate queue
// Only accepts calls from the RocketMinipoolManager contract
function enqueueMinipool(MinipoolDeposit _depositType, address _minipool) override external onlyLatestContract("rocketMinipoolQueue", address(this)) onlyLatestContract("rocketMinipoolManager", msg.sender) {
Expand Down Expand Up @@ -143,6 +109,8 @@ contract RocketMinipoolQueue is RocketBase, RocketMinipoolQueueInterface {

// Remove a minipool from a queue
// Only accepts calls from registered minipools
// Note: this removal is made computationally efficient by swapping with the last item in the
// queue. This is acceptable because removing minipools should be rare.
function removeMinipool(MinipoolDeposit _depositType) override external onlyLatestContract("rocketMinipoolQueue", address(this)) onlyRegisteredMinipool(msg.sender) {
// Remove minipool from queue
if (_depositType == MinipoolDeposit.Half) { return removeMinipool(queueKeyHalf, msg.sender); }
Expand Down
3 changes: 0 additions & 3 deletions contracts/interface/minipool/RocketMinipoolQueueInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import "../../types/MinipoolDeposit.sol";
interface RocketMinipoolQueueInterface {
function getTotalLength() external view returns (uint256);
function getLength(MinipoolDeposit _depositType) external view returns (uint256);
function getTotalCapacity() external view returns (uint256);
function getEffectiveCapacity() external view returns (uint256);
function getNextCapacity() external view returns (uint256);
function getNextDeposit() external view returns (MinipoolDeposit, uint256);
function enqueueMinipool(MinipoolDeposit _depositType, address _minipool) external;
function dequeueMinipool() external returns (address minipoolAddress);
function dequeueMinipoolByDeposit(MinipoolDeposit _depositType) external returns (address minipoolAddress);
Expand Down
6 changes: 2 additions & 4 deletions test/deposit/scenario-assign-deposits.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,9 @@ export async function assignDeposits(txOptions) {
function getMinipoolQueueDetails() {
return Promise.all([
rocketMinipoolQueue.getTotalLength.call(),
rocketMinipoolQueue.getTotalCapacity.call(),
]).then(
([totalLength, totalCapacity]) =>
({totalLength, totalCapacity})
([totalLength]) =>
({totalLength})
);
}

Expand All @@ -94,7 +93,6 @@ export async function assignDeposits(txOptions) {

// Check minipool queues
assert(queue2.totalLength.eq(queue1.totalLength.sub(web3.utils.toBN(expectedDepositAssignments))), 'Incorrect updated minipool queue length');
assert(queue2.totalCapacity.eq(queue1.totalCapacity.sub(expectedEthAssigned)), 'Incorrect updated minipool queue capacity');

}