Skip to content

Commit

Permalink
feat: add more role granularity
Browse files Browse the repository at this point in the history
  • Loading branch information
failingtwice committed Feb 12, 2025
1 parent 7e1f108 commit 2688545
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 45 deletions.
56 changes: 22 additions & 34 deletions contracts/0.8.25/vaults/Delegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,6 @@ import {Dashboard} from "./Dashboard.sol";
/**
* @title Delegation
* @notice This contract is a contract-owner of StakingVault and includes an additional delegation layer.
*
* The delegation hierarchy is as follows:
* - DEFAULT_ADMIN_ROLE is the underlying owner of StakingVault;
* - NODE_OPERATOR_MANAGER_ROLE is the node operator manager of StakingVault; and itself is the role admin,
* and the DEFAULT_ADMIN_ROLE cannot assign NODE_OPERATOR_MANAGER_ROLE;
* - NODE_OPERATOR_FEE_CLAIMER_ROLE is the role that can claim node operator fee; is assigned by NODE_OPERATOR_MANAGER_ROLE;
*
* Additionally, the following roles are assigned by DEFAULT_ADMIN_ROLE:
* - CURATOR_ROLE is the curator of StakingVault and perfoms some operations on behalf of DEFAULT_ADMIN_ROLE;
* - FUND_WITHDRAW_ROLE funds and withdraws from the StakingVault;
* - MINT_BURN_ROLE mints and burns shares of stETH backed by the StakingVault;
*
* The curator and node operator have their respective fees.
* The feeBP is the percentage (in basis points) of the StakingVault rewards.
* The unclaimed fee is the amount of ether that is owed to the curator or node operator based on the feeBP.
*/
contract Delegation is Dashboard {
/**
Expand All @@ -33,31 +18,33 @@ contract Delegation is Dashboard {
uint256 private constant MAX_FEE_BP = TOTAL_BASIS_POINTS;

/**
* @notice Curator role:
* - sets curator fee;
* - claims curator fee;
* - confirms confirm lifetime;
* - confirms node operator fee;
* - confirms ownership transfer;
* - pauses deposits to beacon chain;
* - resumes deposits to beacon chain.
* @notice Sets curator fee.
*/
bytes32 public constant CURATOR_FEE_SET_ROLE = keccak256("vaults.Delegation.CuratorFeeSetRole");

/**
* @notice Claims curator fee.
*/
bytes32 public constant CURATOR_ROLE = keccak256("vaults.Delegation.CuratorRole");
bytes32 public constant CURATOR_FEE_CLAIM_ROLE = keccak256("vaults.Delegation.CuratorFeeClaimRole");

/**
* @notice Node operator manager role:
* - confirms confirm lifetime;
* - confirms node operator fee;
* - confirms ownership transfer;
* - assigns NODE_OPERATOR_FEE_CLAIMER_ROLE.
* - assigns NODE_OPERATOR_FEE_CONFIRM_ROLE;
* - assigns NODE_OPERATOR_FEE_CLAIM_ROLE.
*/
bytes32 public constant NODE_OPERATOR_MANAGER_ROLE = keccak256("vaults.Delegation.NodeOperatorManagerRole");

/**
* @notice Node operator fee claimer role:
* - claims node operator fee.
* @notice Confirms node operator fee.
*/
bytes32 public constant NODE_OPERATOR_FEE_CONFIRM_ROLE = keccak256("vaults.Delegation.NodeOperatorFeeConfirmRole");

/**
* @notice Claims node operator fee.
*/
bytes32 public constant NODE_OPERATOR_FEE_CLAIMER_ROLE = keccak256("vaults.Delegation.NodeOperatorFeeClaimerRole");
bytes32 public constant NODE_OPERATOR_FEE_CLAIM_ROLE = keccak256("vaults.Delegation.NodeOperatorFeeClaimRole");

/**
* @notice Curator fee in basis points; combined with node operator fee cannot exceed 100%.
Expand Down Expand Up @@ -105,7 +92,8 @@ contract Delegation is Dashboard {
// at the end of the initialization
_grantRole(NODE_OPERATOR_MANAGER_ROLE, _defaultAdmin);
_setRoleAdmin(NODE_OPERATOR_MANAGER_ROLE, NODE_OPERATOR_MANAGER_ROLE);
_setRoleAdmin(NODE_OPERATOR_FEE_CLAIMER_ROLE, NODE_OPERATOR_MANAGER_ROLE);
_setRoleAdmin(NODE_OPERATOR_FEE_CONFIRM_ROLE, NODE_OPERATOR_MANAGER_ROLE);
_setRoleAdmin(NODE_OPERATOR_FEE_CLAIM_ROLE, NODE_OPERATOR_MANAGER_ROLE);
}

Check failure

Code scanning / Slither

Unprotected Initialize High

Function Delegation.initialize(address,uint256) is an unprotected initializer.

/**
Expand Down Expand Up @@ -168,7 +156,7 @@ contract Delegation is Dashboard {
* The function will revert if the curator fee is unclaimed.
* @param _newCuratorFeeBP The new curator fee in basis points.
*/
function setCuratorFeeBP(uint256 _newCuratorFeeBP) external onlyRole(DEFAULT_ADMIN_ROLE) {
function setCuratorFeeBP(uint256 _newCuratorFeeBP) external onlyRole(CURATOR_FEE_SET_ROLE) {
if (_newCuratorFeeBP + nodeOperatorFeeBP > MAX_FEE_BP) revert CombinedFeesExceed100Percent();
if (curatorUnclaimedFee() > 0) revert CuratorFeeUnclaimed();
uint256 oldCuratorFeeBP = curatorFeeBP;
Expand Down Expand Up @@ -198,7 +186,7 @@ contract Delegation is Dashboard {
* @notice Claims the curator fee.
* @param _recipient The address to which the curator fee will be sent.
*/
function claimCuratorFee(address _recipient) external onlyRole(CURATOR_ROLE) {
function claimCuratorFee(address _recipient) external onlyRole(CURATOR_FEE_CLAIM_ROLE) {
uint256 fee = curatorUnclaimedFee();
curatorFeeClaimedReport = stakingVault().latestReport();
_claimFee(_recipient, fee);
Expand All @@ -210,7 +198,7 @@ contract Delegation is Dashboard {
* although NODE_OPERATOR_MANAGER_ROLE is the admin role for NODE_OPERATOR_FEE_CLAIMER_ROLE.
* @param _recipient The address to which the node operator fee will be sent.
*/
function claimNodeOperatorFee(address _recipient) external onlyRole(NODE_OPERATOR_FEE_CLAIMER_ROLE) {
function claimNodeOperatorFee(address _recipient) external onlyRole(NODE_OPERATOR_FEE_CLAIM_ROLE) {
uint256 fee = nodeOperatorUnclaimedFee();
nodeOperatorFeeClaimedReport = stakingVault().latestReport();
_claimFee(_recipient, fee);
Expand Down Expand Up @@ -267,7 +255,7 @@ contract Delegation is Dashboard {
*/
function _confirmingRoles() internal pure override returns (bytes32[] memory roles) {
roles = new bytes32[](2);
roles[0] = CURATOR_ROLE;
roles[0] = DEFAULT_ADMIN_ROLE;
roles[1] = NODE_OPERATOR_MANAGER_ROLE;
}

Expand Down
27 changes: 16 additions & 11 deletions contracts/0.8.25/vaults/VaultFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ struct DelegationConfig {
address depositResumer;
address exitRequester;
address disconnecter;
address curator;
address curatorFeeSetter;
address curatorFeeClaimer;
address nodeOperatorManager;
address nodeOperatorFeeClaimer;
address nodeOperatorFeeConfirm;
address nodeOperatorFeeClaim;
uint16 curatorFeeBP;
uint16 nodeOperatorFeeBP;
uint256 confirmLifetime;
Expand All @@ -50,8 +52,6 @@ contract VaultFactory {
DelegationConfig calldata _delegationConfig,
bytes calldata _stakingVaultInitializerExtraParams
) external returns (IStakingVault vault, Delegation delegation) {
if (_delegationConfig.curator == address(0)) revert ZeroArgument("curator");

// create StakingVault
vault = IStakingVault(address(new BeaconProxy(BEACON, "")));

Expand All @@ -69,7 +69,8 @@ contract VaultFactory {
// initialize Delegation
delegation.initialize(address(this), _delegationConfig.confirmLifetime);

// setup roles
// setup roles from config
// basic permissions to the staking vault
delegation.grantRole(delegation.DEFAULT_ADMIN_ROLE(), _delegationConfig.defaultAdmin);
delegation.grantRole(delegation.FUND_ROLE(), _delegationConfig.funder);
delegation.grantRole(delegation.WITHDRAW_ROLE(), _delegationConfig.withdrawer);
Expand All @@ -80,20 +81,24 @@ contract VaultFactory {
delegation.grantRole(delegation.RESUME_BEACON_CHAIN_DEPOSITS_ROLE(), _delegationConfig.depositResumer);
delegation.grantRole(delegation.REQUEST_VALIDATOR_EXIT_ROLE(), _delegationConfig.exitRequester);
delegation.grantRole(delegation.VOLUNTARY_DISCONNECT_ROLE(), _delegationConfig.disconnecter);
delegation.grantRole(delegation.CURATOR_ROLE(), _delegationConfig.curator);
// delegation roles
delegation.grantRole(delegation.CURATOR_FEE_SET_ROLE(), _delegationConfig.curatorFeeSetter);
delegation.grantRole(delegation.CURATOR_FEE_CLAIM_ROLE(), _delegationConfig.curatorFeeClaimer);
delegation.grantRole(delegation.NODE_OPERATOR_MANAGER_ROLE(), _delegationConfig.nodeOperatorManager);
delegation.grantRole(delegation.NODE_OPERATOR_FEE_CLAIMER_ROLE(), _delegationConfig.nodeOperatorFeeClaimer);
delegation.grantRole(delegation.NODE_OPERATOR_FEE_CLAIM_ROLE(), _delegationConfig.nodeOperatorFeeClaim);
delegation.grantRole(delegation.NODE_OPERATOR_FEE_CONFIRM_ROLE(), _delegationConfig.nodeOperatorFeeConfirm);

// grant temporary roles to factory
delegation.grantRole(delegation.CURATOR_ROLE(), address(this));
delegation.grantRole(delegation.NODE_OPERATOR_MANAGER_ROLE(), address(this));
// grant temporary roles to factory for setting fees
delegation.grantRole(delegation.CURATOR_FEE_SET_ROLE(), address(this));
delegation.grantRole(delegation.NODE_OPERATOR_FEE_CONFIRM_ROLE(), address(this));

// set fees
delegation.setCuratorFeeBP(_delegationConfig.curatorFeeBP);
delegation.setNodeOperatorFeeBP(_delegationConfig.nodeOperatorFeeBP);

// revoke temporary roles from factory
delegation.revokeRole(delegation.CURATOR_ROLE(), address(this));
delegation.revokeRole(delegation.CURATOR_FEE_SET_ROLE(), address(this));
delegation.revokeRole(delegation.NODE_OPERATOR_FEE_CONFIRM_ROLE(), address(this));
delegation.revokeRole(delegation.NODE_OPERATOR_MANAGER_ROLE(), address(this));
delegation.revokeRole(delegation.DEFAULT_ADMIN_ROLE(), address(this));

Expand Down

0 comments on commit 2688545

Please sign in to comment.