Skip to content

Commit

Permalink
Split parameter for max stake for withdrawals and max stake for votin…
Browse files Browse the repository at this point in the history
…g power, add a governance enable switch
  • Loading branch information
kanewallmann committed Mar 13, 2024
1 parent 1845a09 commit fd8001d
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 13 deletions.
10 changes: 10 additions & 0 deletions contracts/contract/dao/protocol/RocketDAOProtocol.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contract RocketDAOProtocol is RocketBase, RocketDAOProtocolInterface {
event BootstrapSecurityInvite(string id, address memberAddress, uint256 time);
event BootstrapSecurityKick(address memberAddress, uint256 time);
event BootstrapDisabled(uint256 time);
event BootstrapProtocolDAOEnabled(uint256 block, uint256 time);

// The namespace for any data stored in the network DAO (do not change)
string constant internal daoNameSpace = "dao.protocol.";
Expand Down Expand Up @@ -113,8 +114,17 @@ contract RocketDAOProtocol is RocketBase, RocketDAOProtocolInterface {

/// @notice Bootstrap mode - Disable RP Access (only RP can call this to hand over full control to the DAO)
function bootstrapDisable(bool _confirmDisableBootstrapMode) override external onlyGuardian onlyBootstrapMode onlyLatestContract("rocketDAOProtocol", address(this)) {
// Prevent disabling bootstrap if on-chain governance has not been enabled
require(getUint(keccak256(abi.encodePacked("protocol.dao.enabled.block"))) > 0, "On-chain governance must be enabled first");
// Disable bootstrap
require(_confirmDisableBootstrapMode == true, "You must confirm disabling bootstrap mode, it can only be done once!");
setBool(keccak256(abi.encodePacked(daoNameSpace, "bootstrapmode.disabled")), true);
emit BootstrapDisabled(block.timestamp);
}

/// @notice Bootstrap mode - Enables on-chain governance proposals
function bootstrapEnableGovernance() override external onlyGuardian onlyLatestContract("rocketDAOProtocol", address(this)) {
setUint(keccak256(abi.encodePacked("protocol.dao.enabled.block")), block.number);
emit BootstrapProtocolDAOEnabled(block.number, block.timestamp);
}
}
5 changes: 5 additions & 0 deletions contracts/contract/dao/protocol/RocketDAOProtocolProposal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ contract RocketDAOProtocolProposal is RocketBase, RocketDAOProtocolProposalInter
/// @param _blockNumber The block number the proposal is being made for
/// @param _treeNodes A merkle pollard generated at _blockNumber for the voting power state of the DAO
function propose(string memory _proposalMessage, bytes calldata _payload, uint32 _blockNumber, Types.Node[] calldata _treeNodes) override external onlyRegisteredNode(msg.sender) onlyLatestContract("rocketDAOProtocolProposal", address(this)) returns (uint256) {
// Check on-chain governance has been enabled
{
uint256 enabledBlock = getUint(keccak256(abi.encodePacked("protocol.dao.enabled.block")));
require(enabledBlock != 0 && _blockNumber >= enabledBlock, "DAO has not been enabled");
}
// Calculate total voting power by summing the pollard
uint256 totalVotingPower = 0;
uint256 treeNodesLength = _treeNodes.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ contract RocketDAOProtocolSettingsNode is RocketDAOProtocolSettings, RocketDAOPr
setSettingBool("node.vacant.minipools.enabled", false);
setSettingUint("node.per.minipool.stake.minimum", 0.1 ether); // 10% of user ETH value (matched ETH)
setSettingUint("node.per.minipool.stake.maximum", 1.5 ether); // 150% of node ETH value (provided ETH)
setSettingUint("node.voting.power.stake.maximum", 1.5 ether); // 150% of node ETH value (provided ETH)
// Settings initialised
setBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")), true);
}
Expand All @@ -31,7 +32,7 @@ contract RocketDAOProtocolSettingsNode is RocketDAOProtocolSettings, RocketDAOPr
/// @notice Update a setting, overrides inherited setting method with extra checks for this contract
function setSettingUint(string memory _settingPath, uint256 _value) override public onlyDAOProtocolProposal {
bytes32 settingKey = keccak256(bytes(_settingPath));
if(settingKey == keccak256(bytes("node.per.minipool.stake.maximum"))) {
if(settingKey == keccak256(bytes("node.voting.power.stake.maximum"))) {
// Redirect the setting change to push a new value into the snapshot system instead
RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
rocketNetworkSnapshots.push(settingKey, uint32(block.number), uint224(_value));
Expand Down Expand Up @@ -68,9 +69,13 @@ contract RocketDAOProtocolSettingsNode is RocketDAOProtocolSettings, RocketDAOPr

// Maximum RPL stake per minipool as a fraction of assigned user ETH value
function getMaximumPerMinipoolStake() override external view returns (uint256) {
bytes32 settingKey = keccak256(bytes("node.per.minipool.stake.maximum"));
return getSettingUint("node.per.minipool.stake.maximum");
}

// Maximum staked RPL that applies to voting power per minipool as a fraction of assigned user ETH value
function getMaximumStakeForVotingPower() override external view returns (uint256) {
bytes32 settingKey = keccak256(bytes("node.voting.power.stake.maximum"));
RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
return uint256(rocketNetworkSnapshots.latestValue(settingKey));
}

}
22 changes: 18 additions & 4 deletions contracts/contract/network/RocketNetworkVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import "../../interface/network/RocketNetworkPricesInterface.sol";
import "../../interface/minipool/RocketMinipoolManagerInterface.sol";
import "../../interface/util/AddressSetStorageInterface.sol";
import "../../interface/network/RocketNetworkVotingInterface.sol";
import "../../interface/node/RocketNodeManagerInterface.sol";

/// @notice Accounting for snapshotting of governance related values based on block numbers
contract RocketNetworkVoting is RocketBase, RocketNetworkVotingInterface {
Expand All @@ -28,8 +29,21 @@ contract RocketNetworkVoting is RocketBase, RocketNetworkVotingInterface {
priceKey = keccak256("network.prices.rpl");
}

/// @notice Unlocks a node operator's voting power (only required for node operators who registered before governance structure was in place)
/// @notice Unlocks a node operator's voting power (only required for node operators who registered before
/// governance structure was in place). Sets delegate to self.
function initialiseVoting() onlyRegisteredNode(msg.sender) external override {
_initialiseVoting(msg.sender);
}

/// @notice Unlocks a node operator's voting power (only required for node operators who registered before
/// governance structure was in place).
/// @param _delegate The node operator's desired delegate for their voting power
function initialiseVoting(address _delegate) onlyRegisteredNode(msg.sender) onlyRegisteredNode(_delegate) external override {
_initialiseVoting(_delegate);
}

/// @dev Initialises the snapshot values for the caller to participate in the on-chain governance
function _initialiseVoting(address _delegate) private {
// Check if already registered
require (!getBool(keccak256(abi.encodePacked("node.voting.enabled", msg.sender))), "Already registered");
setBool(keccak256(abi.encodePacked("node.voting.enabled", msg.sender)), true);
Expand All @@ -55,7 +69,7 @@ contract RocketNetworkVoting is RocketBase, RocketNetworkVotingInterface {

// Set starting delegate to themself
key = keccak256(abi.encodePacked("node.delegate", msg.sender));
rocketNetworkSnapshots.push(key, uint32(block.number), uint224(uint160(msg.sender)));
rocketNetworkSnapshots.push(key, uint32(block.number), uint224(uint160(_delegate)));
}

function getVotingInitialised(address _nodeAddress) external override view returns (bool) {
Expand Down Expand Up @@ -109,7 +123,7 @@ contract RocketNetworkVoting is RocketBase, RocketNetworkVotingInterface {
uint256 rplStake = uint256(rocketNetworkSnapshots.lookupRecent(key, _block, 5));

// Get RPL max stake percent
key = keccak256(bytes("node.per.minipool.stake.maximum"));
key = keccak256(bytes("node.voting.power.stake.maximum"));
uint256 maximumStakePercent = uint256(rocketNetworkSnapshots.lookupRecent(key, _block, 2));

return calculateVotingPower(rplStake, ethProvided, rplPrice, maximumStakePercent);
Expand All @@ -126,7 +140,7 @@ contract RocketNetworkVoting is RocketBase, RocketNetworkVotingInterface {
return Math.sqrt(_rplStake);
}

function setDelegate(address _newDelegate) external override onlyRegisteredNode(msg.sender) {
function setDelegate(address _newDelegate) external override onlyRegisteredNode(msg.sender) onlyRegisteredNode(_newDelegate) {
RocketNetworkSnapshotsInterface rocketNetworkSnapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots"));
bytes32 key = keccak256(abi.encodePacked("node.delegate", msg.sender));
rocketNetworkSnapshots.push(key, uint32(block.number), uint224(uint160(_newDelegate)));
Expand Down
15 changes: 9 additions & 6 deletions contracts/contract/upgrade/RocketUpgradeOneDotThree.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "../RocketBase.sol";
import "../../interface/network/RocketNetworkSnapshotsInterface.sol";
import "../../interface/network/RocketNetworkPricesInterface.sol";
import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol";
import "../../interface/util/AddressSetStorageInterface.sol";

/// @notice Transient contract to upgrade Rocket Pool with the Houston set of contract upgrades
contract RocketUpgradeOneDotThree is RocketBase {
Expand Down Expand Up @@ -164,9 +165,6 @@ contract RocketUpgradeOneDotThree is RocketBase {
require(!executed, "Already executed");
executed = true;

RocketDAOProtocolSettingsNodeInterface rocketDAOProtocolSettingsNode = RocketDAOProtocolSettingsNodeInterface(getContractAddress("rocketDAOProtocolSettingsNode"));
uint224 maxPerMinipoolStake = uint224(rocketDAOProtocolSettingsNode.getMaximumPerMinipoolStake());

// Upgrade contracts
_upgradeContract("rocketDAOProtocol", newRocketDAOProtocol, newRocketDAOProtocolAbi);
_upgradeContract("rocketDAOProtocolProposals", newRocketDAOProtocolProposals, newRocketDAOProtocolProposalsAbi);
Expand Down Expand Up @@ -250,9 +248,14 @@ contract RocketUpgradeOneDotThree is RocketBase {
bytes32 snapshotKey = keccak256("network.prices.rpl");
rocketNetworkSnapshots.push(snapshotKey, uint32(block.number), uint224(rocketNetworkPrices.getRPLPrice()));

// Add snapshot entry for maximum RPL stake
snapshotKey = keccak256(bytes("node.per.minipool.stake.maximum"));
rocketNetworkSnapshots.push(snapshotKey, uint32(block.number), maxPerMinipoolStake);
// Add snapshot entry for maximum RPL stake voting power (150%)
snapshotKey = keccak256(bytes("node.voting.power.stake.maximum"));
rocketNetworkSnapshots.push(snapshotKey, uint32(block.number), 1.5 ether);

// Add node count snapshot entry
AddressSetStorageInterface addressSetStorage = AddressSetStorageInterface(getContractAddress("addressSetStorage"));
bytes32 nodeIndexKey = keccak256(abi.encodePacked("nodes.index"));
rocketNetworkSnapshots.push(keccak256(abi.encodePacked("node.count")), uint32(block.number), uint224(addressSetStorage.getCount(nodeIndexKey)));

// Set a protocol version value in storage for convenience with bindings
setString(keccak256(abi.encodePacked("protocol.version")), "1.3.0");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ interface RocketDAOProtocolInterface {
function bootstrapSecurityInvite(string memory _id, address _memberAddress) external;
function bootstrapSecurityKick(address _memberAddress) external;
function bootstrapDisable(bool _confirmDisableBootstrapMode) external;
function bootstrapEnableGovernance() external;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ interface RocketDAOProtocolSettingsNodeInterface {
function getVacantMinipoolsEnabled() external view returns (bool);
function getMinimumPerMinipoolStake() external view returns (uint256);
function getMaximumPerMinipoolStake() external view returns (uint256);
function getMaximumStakeForVotingPower() external view returns (uint256);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >0.5.0 <0.9.0;

interface RocketNetworkVotingInterface {
function initialiseVoting() external;
function initialiseVoting(address _delegate) external;
function getVotingInitialised(address _nodeAddress) external view returns (bool);
function getNodeCount(uint32 _block) external view returns (uint256);
function getVotingPower(address _nodeAddress, uint32 _block) external view returns (uint256);
Expand Down
Loading

0 comments on commit fd8001d

Please sign in to comment.