-
Notifications
You must be signed in to change notification settings - Fork 207
/
RocketMinipoolQueue.sol
245 lines (212 loc) · 12.4 KB
/
RocketMinipoolQueue.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.7.6;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/math/SignedSafeMath.sol";
import "@openzeppelin/contracts/utils/SafeCast.sol";
import "../RocketBase.sol";
import "../../interface/minipool/RocketMinipoolInterface.sol";
import "../../interface/minipool/RocketMinipoolQueueInterface.sol";
import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol";
import "../../interface/util/AddressQueueStorageInterface.sol";
import "../../types/MinipoolDeposit.sol";
/// @notice Minipool queueing for deposit assignment
contract RocketMinipoolQueue is RocketBase, RocketMinipoolQueueInterface {
// Libs
using SafeMath for uint;
using SignedSafeMath for int;
// Constants
bytes32 private constant queueKeyFull = keccak256("minipools.available.full");
bytes32 private constant queueKeyHalf = keccak256("minipools.available.half");
bytes32 private constant queueKeyVariable = keccak256("minipools.available.variable");
// Events
event MinipoolEnqueued(address indexed minipool, bytes32 indexed queueId, uint256 time);
event MinipoolDequeued(address indexed minipool, bytes32 indexed queueId, uint256 time);
event MinipoolRemoved(address indexed minipool, bytes32 indexed queueId, uint256 time);
constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
version = 2;
}
/// @notice Get the total combined length of the queues
function getTotalLength() override external view returns (uint256) {
return (
getLengthLegacy(queueKeyFull)
).add(
getLengthLegacy(queueKeyHalf)
).add(
getLength()
);
}
/// @notice Returns true if there are any legacy minipools in the queue
function getContainsLegacy() override external view returns (bool) {
return getLengthLegacy(queueKeyFull).add(getLengthLegacy(queueKeyHalf)) > 0;
}
/// @notice Get the length of a given queue. Returns 0 for invalid queues
/// @param _depositType Which queue to query the length of
function getLengthLegacy(MinipoolDeposit _depositType) override external view returns (uint256) {
if (_depositType == MinipoolDeposit.Full) { return getLengthLegacy(queueKeyFull); }
if (_depositType == MinipoolDeposit.Half) { return getLengthLegacy(queueKeyHalf); }
return 0;
}
/// @dev Returns a queue length by internal key representation
/// @param _key The internal key representation of the queue to query the length of
function getLengthLegacy(bytes32 _key) private view returns (uint256) {
AddressQueueStorageInterface addressQueueStorage = AddressQueueStorageInterface(getContractAddress("addressQueueStorage"));
return addressQueueStorage.getLength(_key);
}
/// @notice Gets the length of the variable (global) queue
function getLength() override public view returns (uint256) {
AddressQueueStorageInterface addressQueueStorage = AddressQueueStorageInterface(getContractAddress("addressQueueStorage"));
return addressQueueStorage.getLength(queueKeyVariable);
}
/// @notice Get the total combined capacity of the queues
function getTotalCapacity() override external view returns (uint256) {
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
return (
getLengthLegacy(queueKeyFull).mul(rocketDAOProtocolSettingsMinipool.getFullDepositUserAmount())
).add(
getLengthLegacy(queueKeyHalf).mul(rocketDAOProtocolSettingsMinipool.getHalfDepositUserAmount())
).add(
getVariableCapacity()
);
}
/// @notice 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"));
return (
getLengthLegacy(queueKeyFull).mul(rocketDAOProtocolSettingsMinipool.getFullDepositUserAmount())
).add(
getLengthLegacy(queueKeyHalf).mul(rocketDAOProtocolSettingsMinipool.getHalfDepositUserAmount())
).add(
getVariableCapacity()
);
}
/// @dev Get the ETH capacity of the variable queue
function getVariableCapacity() internal view returns (uint256) {
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
return getLength().mul(rocketDAOProtocolSettingsMinipool.getVariableDepositAmount());
}
/// @notice Get the capacity of the next available minipool. Returns 0 if no minipools are available
function getNextCapacityLegacy() override external view returns (uint256) {
RocketDAOProtocolSettingsMinipoolInterface rocketDAOProtocolSettingsMinipool = RocketDAOProtocolSettingsMinipoolInterface(getContractAddress("rocketDAOProtocolSettingsMinipool"));
if (getLengthLegacy(queueKeyHalf) > 0) { return rocketDAOProtocolSettingsMinipool.getHalfDepositUserAmount(); }
if (getLengthLegacy(queueKeyFull) > 0) { return rocketDAOProtocolSettingsMinipool.getFullDepositUserAmount(); }
return 0;
}
/// @notice 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 getNextDepositLegacy() override external view returns (MinipoolDeposit, uint256) {
uint256 length = getLengthLegacy(queueKeyHalf);
if (length > 0) { return (MinipoolDeposit.Half, length); }
length = getLengthLegacy(queueKeyFull);
if (length > 0) { return (MinipoolDeposit.Full, length); }
return (MinipoolDeposit.None, 0);
}
/// @dev Add a minipool to the end of the appropriate queue. Only accepts calls from the RocketMinipoolManager contract
/// @param _minipool Address of the minipool to add to the queue
function enqueueMinipool(address _minipool) override external onlyLatestContract("rocketMinipoolQueue", address(this)) onlyLatestContract("rocketNodeDeposit", msg.sender) {
// Enqueue
AddressQueueStorageInterface addressQueueStorage = AddressQueueStorageInterface(getContractAddress("addressQueueStorage"));
addressQueueStorage.enqueueItem(queueKeyVariable, _minipool);
// Emit enqueued event
emit MinipoolEnqueued(_minipool, queueKeyVariable, block.timestamp);
}
/// @dev Dequeues a minipool from a legacy queue
/// @param _depositType The queue to dequeue a minipool from
function dequeueMinipoolByDepositLegacy(MinipoolDeposit _depositType) override external onlyLatestContract("rocketMinipoolQueue", address(this)) onlyLatestContract("rocketDepositPool", msg.sender) returns (address minipoolAddress) {
if (_depositType == MinipoolDeposit.Half) { return dequeueMinipool(queueKeyHalf); }
if (_depositType == MinipoolDeposit.Full) { return dequeueMinipool(queueKeyFull); }
require(false, "No minipools are available");
}
/// @dev Dequeues multiple minipools from the variable queue and returns them all
/// @param _maxToDequeue The maximum number of items to dequeue
function dequeueMinipools(uint256 _maxToDequeue) override external onlyLatestContract("rocketMinipoolQueue", address(this)) onlyLatestContract("rocketDepositPool", msg.sender) returns (address[] memory minipoolAddress) {
uint256 queueLength = getLength();
uint256 count = _maxToDequeue;
if (count > queueLength) {
count = queueLength;
}
address[] memory minipoolAddresses = new address[](count);
for (uint256 i = 0; i < count; i++) {
RocketMinipoolInterface minipool = RocketMinipoolInterface(dequeueMinipool(queueKeyVariable));
minipoolAddresses[i] = address(minipool);
}
return minipoolAddresses;
}
/// @dev Dequeues a minipool from a queue given an internal key
/// @param _key The internal key representation of the queue from which to dequeue a minipool from
function dequeueMinipool(bytes32 _key) private returns (address) {
// Dequeue
AddressQueueStorageInterface addressQueueStorage = AddressQueueStorageInterface(getContractAddress("addressQueueStorage"));
address minipool = addressQueueStorage.dequeueItem(_key);
// Emit dequeued event
emit MinipoolDequeued(minipool, _key, block.timestamp);
// Return
return minipool;
}
/// @dev Remove a minipool from a queue. Only accepts calls from registered minipools
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); }
if (_depositType == MinipoolDeposit.Full) { return removeMinipool(queueKeyFull, msg.sender); }
if (_depositType == MinipoolDeposit.Variable) { return removeMinipool(queueKeyVariable, msg.sender); }
require(false, "Invalid minipool deposit type");
}
/// @dev Removes a minipool from a queue given an internal key
/// @param _key The internal key representation of the queue from which to remove a minipool from
/// @param _minipool The address of a minipool to remove from the specified queue
function removeMinipool(bytes32 _key, address _minipool) private {
// Remove
AddressQueueStorageInterface addressQueueStorage = AddressQueueStorageInterface(getContractAddress("addressQueueStorage"));
addressQueueStorage.removeItem(_key, _minipool);
// Emit removed event
emit MinipoolRemoved(_minipool, _key, block.timestamp);
}
/// @notice Returns the minipool address of the minipool in the global queue at a given index
/// @param _index The index into the queue to retrieve
function getMinipoolAt(uint256 _index) override external view returns(address) {
AddressQueueStorageInterface addressQueueStorage = AddressQueueStorageInterface(getContractAddress("addressQueueStorage"));
// Check if index is in the half queue
uint256 halfLength = addressQueueStorage.getLength(queueKeyHalf);
if (_index < halfLength) {
return addressQueueStorage.getItem(queueKeyHalf, _index);
}
_index = _index.sub(halfLength);
// Check if index is in the full queue
uint256 fullLength = addressQueueStorage.getLength(queueKeyFull);
if (_index < fullLength) {
return addressQueueStorage.getItem(queueKeyFull, _index);
}
_index = _index.sub(fullLength);
// Check if index is in the full queue
uint256 variableLength = addressQueueStorage.getLength(queueKeyVariable);
if (_index < variableLength) {
return addressQueueStorage.getItem(queueKeyVariable, _index);
}
// Index is out of bounds
return address(0);
}
/// @notice Returns the position a given minipool is in the queue
/// @param _minipool The minipool to query the position of
function getMinipoolPosition(address _minipool) override external view returns (int256) {
AddressQueueStorageInterface addressQueueStorage = AddressQueueStorageInterface(getContractAddress("addressQueueStorage"));
int256 position;
// Check in half queue
position = addressQueueStorage.getIndexOf(queueKeyHalf, _minipool);
if (position != -1) {
return position;
}
int256 offset = SafeCast.toInt256(addressQueueStorage.getLength(queueKeyHalf));
// Check in full queue
position = addressQueueStorage.getIndexOf(queueKeyFull, _minipool);
if (position != -1) {
return offset.add(position);
}
offset = offset.add(SafeCast.toInt256(addressQueueStorage.getLength(queueKeyFull)));
// Check in variable queue
position = addressQueueStorage.getIndexOf(queueKeyVariable, _minipool);
if (position != -1) {
return offset.add(position);
}
// Isn't in the queue
return -1;
}
}