Skip to content

Commit bea151d

Browse files
committed
Fixes and improvements from SP audit and devnet testing
1 parent f95d430 commit bea151d

30 files changed

+439
-155
lines changed

contracts/contract/dao/protocol/settings/RocketDAOProtocolSettingsMinipool.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ contract RocketDAOProtocolSettingsMinipool is RocketDAOProtocolSettings, RocketD
9999
return (_time >= start && _time < (start.add(length)));
100100
}
101101

102+
/// @notice Returns true if the given time has passed the distribute window
103+
function hasUserDistributeWindowPassed(uint256 _time) override external view returns (bool) {
104+
uint256 start = getUserDistributeWindowStart();
105+
uint256 length = getUserDistributeWindowLength();
106+
return _time >= start.add(length);
107+
}
108+
102109
/// @notice Returns the start of the user distribute window
103110
function getUserDistributeWindowStart() override public view returns (uint256) {
104111
return getSettingUint("minipool.user.distribute.window.start");

contracts/contract/dao/protocol/settings/RocketDAOProtocolSettingsNode.sol

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ contract RocketDAOProtocolSettingsNode is RocketDAOProtocolSettings, RocketDAOPr
1212
// Construct
1313
constructor(RocketStorageInterface _rocketStorageAddress) RocketDAOProtocolSettings(_rocketStorageAddress, "node") {
1414
// Set version
15-
version = 2;
15+
version = 3;
1616
// Initialize settings on deployment
1717
if(!getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) {
1818
// Apply settings
1919
setSettingBool("node.registration.enabled", false);
2020
setSettingBool("node.smoothing.pool.registration.enabled", true);
2121
setSettingBool("node.deposit.enabled", false);
22+
setSettingBool("node.vacant.minipools.enabled", false);
2223
setSettingUint("node.per.minipool.stake.minimum", 0.1 ether); // 10% of user ETH value
2324
setSettingUint("node.per.minipool.stake.maximum", 1.5 ether); // 150% of user ETH value
2425
// Settings initialised
@@ -41,6 +42,11 @@ contract RocketDAOProtocolSettingsNode is RocketDAOProtocolSettings, RocketDAOPr
4142
return getSettingBool("node.deposit.enabled");
4243
}
4344

45+
// Vacant minipools currently enabled
46+
function getVacantMinipoolsEnabled() override external view returns (bool) {
47+
return getSettingBool("node.vacant.minipools.enabled");
48+
}
49+
4450
// Minimum RPL stake per minipool as a fraction of assigned user ETH value
4551
function getMinimumPerMinipoolStake() override external view returns (uint256) {
4652
return getSettingUint("node.per.minipool.stake.minimum");

contracts/contract/deposit/RocketDepositPool.sol

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,14 @@ contract RocketDepositPool is RocketBase, RocketDepositPoolInterface, RocketVaul
148148
}
149149

150150
/// @dev Accepts ETH deposit from the node deposit contract (does not mint rETH)
151-
function nodeDeposit() override external payable onlyThisLatestContract onlyLatestContract("rocketNodeDeposit", msg.sender) {
151+
/// @param _totalAmount The total node deposit amount including any credit balance used
152+
function nodeDeposit(uint256 _totalAmount) override external payable onlyThisLatestContract onlyLatestContract("rocketNodeDeposit", msg.sender) {
152153
// Deposit ETH into the vault
153-
rocketVault.depositEther{value: msg.value}();
154+
if (msg.value > 0) {
155+
rocketVault.depositEther{value: msg.value}();
156+
}
154157
// Increase recorded node balance
155-
addUint("deposit.pool.node.balance", msg.value);
158+
addUint("deposit.pool.node.balance", _totalAmount);
156159
}
157160

158161
/// @dev Withdraws ETH from the deposit pool to RocketNodeDeposit contract to be used for a new minipool

contracts/contract/minipool/RocketMinipoolBase.sol

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ contract RocketMinipoolBase is RocketMinipoolStorageLayout {
1414
event DelegateUpgraded(address oldDelegate, address newDelegate, uint256 time);
1515
event DelegateRolledBack(address oldDelegate, address newDelegate, uint256 time);
1616

17+
// Store a reference to the address of RocketMinipoolBase itself to prevent direct calls to this contract
18+
address immutable self;
19+
20+
constructor () {
21+
self = address(this);
22+
}
23+
24+
/// @dev Prevent direct calls to this contract
25+
modifier notSelf() {
26+
require(address(this) != self);
27+
_;
28+
}
29+
1730
/// @dev Only allow access from the owning node address
1831
modifier onlyMinipoolOwner() {
1932
// Only the node operator can upgrade
@@ -23,9 +36,10 @@ contract RocketMinipoolBase is RocketMinipoolStorageLayout {
2336
}
2437

2538
/// @notice Sets up starting delegate contract and then delegates initialisation to it
26-
function initialise(address _nodeAddress) external {
39+
function initialise(address _nodeAddress) external notSelf {
2740
// Check input
2841
require(_nodeAddress != address(0), "Invalid node address");
42+
require(storageState == StorageState.Undefined, "Already initialised");
2943
// Set storage state to uninitialised
3044
storageState = StorageState.Uninitialised;
3145
// Set the current delegate
@@ -39,13 +53,13 @@ contract RocketMinipoolBase is RocketMinipoolStorageLayout {
3953
}
4054

4155
/// @notice Receive an ETH deposit
42-
receive() external payable {
56+
receive() external payable notSelf {
4357
// Emit ether received event
4458
emit EtherReceived(msg.sender, msg.value, block.timestamp);
4559
}
4660

4761
/// @notice Upgrade this minipool to the latest network delegate contract
48-
function delegateUpgrade() external onlyMinipoolOwner {
62+
function delegateUpgrade() external onlyMinipoolOwner notSelf {
4963
// Set previous address
5064
rocketMinipoolDelegatePrev = rocketMinipoolDelegate;
5165
// Set new delegate
@@ -57,7 +71,7 @@ contract RocketMinipoolBase is RocketMinipoolStorageLayout {
5771
}
5872

5973
/// @notice Rollback to previous delegate contract
60-
function delegateRollback() external onlyMinipoolOwner {
74+
function delegateRollback() external onlyMinipoolOwner notSelf {
6175
// Make sure they have upgraded before
6276
require(rocketMinipoolDelegatePrev != address(0x0), "Previous delegate contract is not set");
6377
// Store original
@@ -71,7 +85,7 @@ contract RocketMinipoolBase is RocketMinipoolStorageLayout {
7185

7286
/// @notice Sets the flag to automatically use the latest delegate contract or not
7387
/// @param _setting If true, will always use the latest delegate contract
74-
function setUseLatestDelegate(bool _setting) external onlyMinipoolOwner {
88+
function setUseLatestDelegate(bool _setting) external onlyMinipoolOwner notSelf {
7589
useLatestDelegate = _setting;
7690
}
7791

@@ -96,7 +110,7 @@ contract RocketMinipoolBase is RocketMinipoolStorageLayout {
96110
}
97111

98112
/// @notice Delegates all calls to minipool delegate contract (or latest if flag is set)
99-
fallback(bytes calldata _input) external payable returns (bytes memory) {
113+
fallback(bytes calldata _input) external payable notSelf returns (bytes memory) {
100114
// If useLatestDelegate is set, use the latest delegate contract
101115
address delegateContract = useLatestDelegate ? getContractAddress("rocketMinipoolDelegate") : rocketMinipoolDelegate;
102116
// Check for contract existence

contracts/contract/minipool/RocketMinipoolBondReducer.sol

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ pragma abicoder v2;
55
import "@openzeppelin/contracts/math/SafeMath.sol";
66

77
import "../RocketBase.sol";
8-
import "./RocketMinipoolDelegate.sol";
8+
import "../../interface/minipool/RocketMinipoolInterface.sol";
99
import "../../interface/minipool/RocketMinipoolBondReducerInterface.sol";
10+
import "../../interface/node/RocketNodeDepositInterface.sol";
11+
import "../../interface/dao/node/settings/RocketDAONodeTrustedSettingsMinipoolInterface.sol";
12+
import "../../interface/dao/node/RocketDAONodeTrustedInterface.sol";
1013

1114
/// @notice Handles bond reduction window and trusted node cancellation
1215
contract RocketMinipoolBondReducer is RocketBase, RocketMinipoolBondReducerInterface {
@@ -15,7 +18,7 @@ contract RocketMinipoolBondReducer is RocketBase, RocketMinipoolBondReducerInter
1518
using SafeMath for uint;
1619

1720
// Events
18-
event BeginBondReduction(address indexed minipool, uint256 time);
21+
event BeginBondReduction(address indexed minipool, uint256 newBondAmount, uint256 time);
1922
event CancelReductionVoted(address indexed minipool, address indexed member, uint256 time);
2023
event ReductionCancelled(address indexed minipool, uint256 time);
2124

@@ -26,21 +29,47 @@ contract RocketMinipoolBondReducer is RocketBase, RocketMinipoolBondReducerInter
2629
/// @notice Flags a minipool as wanting to reduce collateral, owner can then call `reduceBondAmount` once waiting
2730
/// period has elapsed
2831
/// @param _minipoolAddress Address of the minipool
29-
function beginReduceBondAmount(address _minipoolAddress) override external onlyLatestContract("rocketMinipoolBondReducer", address(this)) {
30-
RocketMinipoolDelegate minipool = RocketMinipoolDelegate(_minipoolAddress);
32+
/// @param _newBondAmount The new bond amount
33+
function beginReduceBondAmount(address _minipoolAddress, uint256 _newBondAmount) override external onlyLatestContract("rocketMinipoolBondReducer", address(this)) {
34+
RocketMinipoolInterface minipool = RocketMinipoolInterface(_minipoolAddress);
35+
RocketNodeDepositInterface rocketNodeDeposit = RocketNodeDepositInterface(getContractAddress("rocketNodeDeposit"));
3136
require(msg.sender == minipool.getNodeAddress(), "Only minipool owner");
3237
// Check if has been previously cancelled
3338
bool reductionCancelled = getBool(keccak256(abi.encodePacked("minipool.bond.reduction.cancelled", address(minipool))));
3439
require(!reductionCancelled, "This minipool is not allowed to reduce bond");
3540
require(minipool.getStatus() == MinipoolStatus.Staking, "Minipool must be staking");
41+
// Check if new bond amount is valid
42+
require(rocketNodeDeposit.isValidDepositAmount(_newBondAmount), "Invalid bond amount");
43+
uint256 existing = minipool.getNodeDepositBalance();
44+
require(_newBondAmount < existing, "Bond must be lower than current amount");
45+
// Store time and new bond amount
3646
setUint(keccak256(abi.encodePacked("minipool.bond.reduction.time", _minipoolAddress)), block.timestamp);
37-
emit BeginBondReduction(_minipoolAddress, block.timestamp);
47+
setUint(keccak256(abi.encodePacked("minipool.bond.reduction.value", _minipoolAddress)), _newBondAmount);
48+
emit BeginBondReduction(_minipoolAddress, _newBondAmount, block.timestamp);
49+
}
50+
51+
/// @notice Returns the timestamp of when a given minipool began their bond reduction waiting period
52+
/// @param _minipoolAddress Address of the minipool to query
53+
function getReduceBondTime(address _minipoolAddress) override external view returns (uint256) {
54+
return getUint(keccak256(abi.encodePacked("minipool.bond.reduction.time", _minipoolAddress)));
55+
}
56+
57+
/// @notice Returns the new bond that a given minipool has indicated they are reducing to
58+
/// @param _minipoolAddress Address of the minipool to query
59+
function getReduceBondValue(address _minipoolAddress) override external view returns (uint256) {
60+
return getUint(keccak256(abi.encodePacked("minipool.bond.reduction.value", _minipoolAddress)));
61+
}
62+
63+
/// @notice Returns true if the given minipool has had it's bond reduction cancelled by the oDAO
64+
/// @param _minipoolAddress Address of the minipool to query
65+
function getReduceBondCancelled(address _minipoolAddress) override public view returns (bool) {
66+
return getBool(keccak256(abi.encodePacked("minipool.bond.reduction.cancelled", address(_minipoolAddress))));
3867
}
3968

4069
/// @notice Returns whether owner of given minipool can reduce bond amount given the waiting period constraint
4170
/// @param _minipoolAddress Address of the minipool
4271
function canReduceBondAmount(address _minipoolAddress) override public view returns (bool) {
43-
RocketMinipoolDelegate minipool = RocketMinipoolDelegate(_minipoolAddress);
72+
RocketMinipoolInterface minipool = RocketMinipoolInterface(_minipoolAddress);
4473
RocketDAONodeTrustedSettingsMinipoolInterface rocketDAONodeTrustedSettingsMinipool = RocketDAONodeTrustedSettingsMinipoolInterface(getContractAddress("rocketDAONodeTrustedSettingsMinipool"));
4574
uint256 reduceBondTime = getUint(keccak256(abi.encodePacked("minipool.bond.reduction.time", _minipoolAddress)));
4675
return rocketDAONodeTrustedSettingsMinipool.isWithinBondReductionWindow(block.timestamp.sub(reduceBondTime));
@@ -49,8 +78,10 @@ contract RocketMinipoolBondReducer is RocketBase, RocketMinipoolBondReducerInter
4978
/// @notice Can be called by trusted nodes to cancel a reduction in bond if the validator has too low of a balance
5079
/// @param _minipoolAddress Address of the minipool
5180
function voteCancelReduction(address _minipoolAddress) override external onlyTrustedNode(msg.sender) onlyLatestContract("rocketMinipoolBondReducer", address(this)) {
52-
RocketMinipoolDelegate minipool = RocketMinipoolDelegate(_minipoolAddress);
81+
// Prevent calling if consensus has already been reached
82+
require(!getReduceBondCancelled(_minipoolAddress), "Already cancelled");
5383
// Get contracts
84+
RocketMinipoolInterface minipool = RocketMinipoolInterface(_minipoolAddress);
5485
RocketDAONodeTrustedInterface rocketDAONode = RocketDAONodeTrustedInterface(getContractAddress("rocketDAONodeTrusted"));
5586
// Check for multiple votes
5687
bytes32 memberVotedKey = keccak256(abi.encodePacked("minipool.bond.reduction.member.voted", _minipoolAddress, msg.sender));
@@ -69,33 +100,39 @@ contract RocketMinipoolBondReducer is RocketBase, RocketMinipoolBondReducerInter
69100
emit ReductionCancelled(_minipoolAddress, block.timestamp);
70101
setBool(keccak256(abi.encodePacked("minipool.bond.reduction.cancelled", _minipoolAddress)), true);
71102
deleteUint(keccak256(abi.encodePacked("minipool.bond.reduction.time", _minipoolAddress)));
103+
deleteUint(keccak256(abi.encodePacked("minipool.bond.reduction.value", msg.sender)));
72104
} else {
73105
// Increment total
74106
setUint(totalCancelVotesKey, totalCancelVotes);
75107
}
76108
}
77109

78110
/// @notice Called by minipools when they are reducing bond to handle state changes outside the minipool
79-
/// @param _from The previous bond amount
80-
/// @param _to The new bond amount
81-
function reduceBondAmount(uint256 _from, uint256 _to) override external onlyRegisteredMinipool(msg.sender) onlyLatestContract("rocketMinipoolBondReducer", address(this)) {
111+
function reduceBondAmount() override external onlyRegisteredMinipool(msg.sender) onlyLatestContract("rocketMinipoolBondReducer", address(this)) returns (uint256) {
112+
// Get contracts
113+
RocketNodeDepositInterface rocketNodeDeposit = RocketNodeDepositInterface(getContractAddress("rocketNodeDeposit"));
114+
RocketMinipoolInterface minipool = RocketMinipoolInterface(msg.sender);
82115
// Check if has been cancelled
83116
bool reductionCancelled = getBool(keccak256(abi.encodePacked("minipool.bond.reduction.cancelled", address(msg.sender))));
84117
require(!reductionCancelled, "This minipool is not allowed to reduce bond");
85118
// Check wait period is satisfied
86119
require(canReduceBondAmount(msg.sender), "Wait period not satisfied");
87-
// Get contracts
88-
RocketNodeDepositInterface rocketNodeDeposit = RocketNodeDepositInterface(getContractAddress("rocketNodeDeposit"));
89-
RocketMinipoolDelegate minipool = RocketMinipoolDelegate(msg.sender);
90-
// Check the new bond amount is valid
91-
require(rocketNodeDeposit.isValidDepositAmount(_to), "Invalid bond amount");
120+
// Get desired to amount
121+
uint256 newBondAmount = getUint(keccak256(abi.encodePacked("minipool.bond.reduction.value", msg.sender)));
122+
require(rocketNodeDeposit.isValidDepositAmount(newBondAmount), "Invalid bond amount");
92123
// Calculate difference
93-
uint256 delta = _from.sub(_to);
124+
uint256 existingBondAmount = minipool.getNodeDepositBalance();
125+
uint256 delta = existingBondAmount.sub(newBondAmount);
94126
// Get node address
95127
address nodeAddress = minipool.getNodeAddress();
96128
// Increase ETH matched or revert if exceeds limit based on current RPL stake
97129
rocketNodeDeposit.increaseEthMatched(nodeAddress, delta);
98130
// Increase node operator's deposit credit
99131
rocketNodeDeposit.increaseDepositCreditBalance(nodeAddress, delta);
132+
// Clean up state
133+
deleteUint(keccak256(abi.encodePacked("minipool.bond.reduction.time", msg.sender)));
134+
deleteUint(keccak256(abi.encodePacked("minipool.bond.reduction.value", msg.sender)));
135+
// Return
136+
return newBondAmount;
100137
}
101138
}

0 commit comments

Comments
 (0)