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

Create new role to update pool weights #247

Open
wants to merge 4 commits into
base: main
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
7 changes: 5 additions & 2 deletions contracts/PoolSelector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,15 @@ contract PoolSelector is IPoolSelector, AccessControlUpgradeable {

/**
* @notice update the target weights of existing pools
* @dev only `Manager` can call,
* @dev only authorised callers can call,
* @param _poolTargets new target weights of pools
* `_poolTargets` array provide pool target in the same order of poolIDs that are stored in poolIdArray of poolUtils
*/
function updatePoolWeights(uint256[] calldata _poolTargets) external {
UtilLib.onlyManagerRole(msg.sender, staderConfig);
if (!staderConfig.isAllowedToCall(msg.sender, "updatePoolWeights(uint256[])")) {
revert AccessDenied(msg.sender);
}

uint8[] memory poolIdArray = IPoolUtils(staderConfig.getPoolUtils()).getPoolIdArray();
uint256 poolCount = poolIdArray.length;
uint256 poolTargetLength = _poolTargets.length;
Expand Down
5 changes: 4 additions & 1 deletion contracts/PoolUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ contract PoolUtils is IPoolUtils, AccessControlUpgradeable {
* @dev emit an event containing validator pubkey for offchain to exit the validator
*/
function processValidatorExitList(bytes[] calldata _pubkeys) external override {
UtilLib.onlyOperatorRole(msg.sender, staderConfig);
if (!staderConfig.isAllowedToCall(msg.sender, "processValidatorExitList(bytes[])")) {
revert AccessDenied(msg.sender);
}

uint256 exitValidatorCount = _pubkeys.length;
for (uint256 i; i < exitValidatorCount; ) {
emit ExitValidator(_pubkeys[i]);
Expand Down
26 changes: 26 additions & 0 deletions contracts/StaderConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
bytes32 public constant override OPERATOR = keccak256("OPERATOR");

bytes32 public constant SD = keccak256("SD");
bytes32 public constant ETHx = keccak256("ETHx");

Check warning on line 72 in contracts/StaderConfig.sol

View workflow job for this annotation

GitHub Actions / Run linters

Constant name must be in capitalized SNAKE_CASE

mapping(bytes32 => uint256) private constantsMap;
mapping(bytes32 => uint256) private variablesMap;
Expand Down Expand Up @@ -297,6 +297,27 @@
setContract(SD_INCENTIVE_CONTROLLER, _sdIncentiveController);
}

// Access Control
function giveCallPermission(
address contractAddress,
string calldata functionSig,
address accountToPermit
) external override onlyRole(MANAGER) {
bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig));
_grantRole(role, accountToPermit);
emit PermissionGranted(accountToPermit, contractAddress, functionSig);
}

function revokeCallPermission(
address contractAddress,
string calldata functionSig,
address accountToRevoke
) external override onlyRole(MANAGER) {
bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig));
_revokeRole(role, accountToRevoke);
emit PermissionRevoked(accountToRevoke, contractAddress, functionSig);

Check warning on line 318 in contracts/StaderConfig.sol

View check run for this annotation

Codecov / codecov/patch

contracts/StaderConfig.sol#L316-L318

Added lines #L316 - L318 were not covered by tests
}

//Constants Getters

function getStakedEthPerNode() external view override returns (uint256) {
Expand Down Expand Up @@ -537,6 +558,11 @@
return hasRole(OPERATOR, account);
}

function isAllowedToCall(address account, string calldata functionSig) external view override returns (bool) {
bytes32 role = keccak256(abi.encodePacked(msg.sender, functionSig));
return hasRole(role, account);
}

function verifyDepositAndWithdrawLimits() internal view {
if (
!(variablesMap[MIN_DEPOSIT_AMOUNT] != 0 &&
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/IPoolSelector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface IPoolSelector {
error InvalidTargetWeight();
error InvalidNewTargetInput();
error InvalidSumOfPoolWeights();
error AccessDenied(address account);

// Events

Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/IPoolUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface IPoolUtils {
error OperatorIsNotOnboarded();
error InvalidLengthOfSignature();
error ExistingOrMismatchingPoolId();
error AccessDenied(address account);

// Events
event PoolAdded(uint8 indexed poolId, address poolAddress);
Expand Down
12 changes: 12 additions & 0 deletions contracts/interfaces/IStaderConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ interface IStaderConfig {
event SetAccount(bytes32 key, address newAddress);
event SetContract(bytes32 key, address newAddress);
event SetToken(bytes32 key, address newAddress);
event PermissionGranted(address indexed accountToPermit, address indexed contractAddress, string functionSig);
event PermissionRevoked(address indexed accountToRevoke, address indexed contractAddress, string functionSig);

//Contracts
function POOL_UTILS() external view returns (bytes32);
Expand Down Expand Up @@ -171,4 +173,14 @@ interface IStaderConfig {
function onlyManagerRole(address account) external view returns (bool);

function onlyOperatorRole(address account) external view returns (bool);

function isAllowedToCall(address account, string calldata functionSig) external view returns (bool);

function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) external;

function revokeCallPermission(
address contractAddress,
string calldata functionSig,
address accountToRevoke
) external;
}
14 changes: 11 additions & 3 deletions test/foundry_tests/PoolSelector.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ contract PoolSelectorTest is Test {
address staderAdmin;
address staderManager;
address operator;
address configurator;
address naiveAddress;

address staderStakePoolManager;

Expand All @@ -28,6 +30,8 @@ contract PoolSelectorTest is Test {
staderAdmin = vm.addr(100);
staderManager = vm.addr(101);
operator = vm.addr(102);
configurator = vm.addr(116);
naiveAddress = vm.addr(117);
staderStakePoolManager = vm.addr(110);

address ethDepositAddr = vm.addr(103);
Expand Down Expand Up @@ -60,6 +64,9 @@ contract PoolSelectorTest is Test {
staderConfig.grantRole(staderConfig.MANAGER(), staderManager);
staderConfig.grantRole(staderConfig.OPERATOR(), operator);
vm.stopPrank();

vm.prank(staderManager);
staderConfig.giveCallPermission(address(poolSelector), "updatePoolWeights(uint256[])", configurator);
}

function test_JustToIncreaseCoverage() public {
Expand Down Expand Up @@ -95,10 +102,11 @@ contract PoolSelectorTest is Test {
invalidSizePoolWeight[1] = 4000;
invalidSizePoolWeight[2] = 4000;

vm.expectRevert(UtilLib.CallerNotManager.selector);
vm.prank(naiveAddress);
vm.expectRevert(abi.encodeWithSignature("AccessDenied(address)", naiveAddress));
poolSelector.updatePoolWeights(poolWeight);

vm.startPrank(staderManager);
vm.startPrank(configurator);
vm.expectRevert(IPoolSelector.InvalidNewTargetInput.selector);
poolSelector.updatePoolWeights(invalidSizePoolWeight);

Expand All @@ -112,7 +120,7 @@ contract PoolSelectorTest is Test {
uint256[] memory poolWeight = new uint256[](2);
poolWeight[0] = 7000;
poolWeight[1] = 3000;
vm.prank(staderManager);
vm.prank(configurator);
poolSelector.updatePoolWeights(poolWeight);
vm.prank(operator);
poolSelector.updatePoolAllocationMaxSize(1000);
Expand Down
12 changes: 10 additions & 2 deletions test/foundry_tests/PoolUtils.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ contract PoolUtilsTest is Test {
address staderAdmin;
address staderManager;
address operator;
address configurator;
address naiveAddress;

PoolUtils poolUtils;
StaderConfig staderConfig;
Expand All @@ -31,6 +33,8 @@ contract PoolUtilsTest is Test {
staderAdmin = vm.addr(100);
staderManager = vm.addr(101);
operator = vm.addr(102);
configurator = vm.addr(114);
naiveAddress = vm.addr(117);

address ethDepositAddr = vm.addr(103);
nodeRegistry = new NodeRegistryMock(operator);
Expand Down Expand Up @@ -60,6 +64,9 @@ contract PoolUtilsTest is Test {
staderConfig.grantRole(staderConfig.MANAGER(), staderManager);
staderConfig.grantRole(staderConfig.OPERATOR(), operator);
vm.stopPrank();

vm.prank(staderManager);
staderConfig.giveCallPermission(address(poolUtils), "processValidatorExitList(bytes[])", configurator);
}

function test_JustToIncreaseCoverage() public {
Expand Down Expand Up @@ -134,10 +141,11 @@ contract PoolUtilsTest is Test {
pubkey[0] = "0x8faa339ba46c649885ea0fc9c34d32f9d99c5bde336750";
pubkey[1] = "0x8faa339ba46c649885ea0fc9c34d32f9d99c5bde336750";

vm.expectRevert(UtilLib.CallerNotOperator.selector);
vm.prank(naiveAddress);
vm.expectRevert(abi.encodeWithSignature("AccessDenied(address)", naiveAddress));
poolUtils.processValidatorExitList(pubkey);

vm.prank(operator);
vm.prank(configurator);
poolUtils.processValidatorExitList(pubkey);
}

Expand Down
Loading