From cf3c724f5717cab22a930740b05d5735580018a6 Mon Sep 17 00:00:00 2001 From: Szooot Date: Mon, 23 Mar 2026 22:16:50 +0100 Subject: [PATCH] setter for upgrader role --- abis/ValidatorFactory.json | 31 +++++++++++++++++++++ src/ValidatorFactory.sol | 28 +++++++++++++++++++ test/ValidatorFactory.t.sol | 54 +++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/abis/ValidatorFactory.json b/abis/ValidatorFactory.json index b4a515a..d283bc6 100644 --- a/abis/ValidatorFactory.json +++ b/abis/ValidatorFactory.json @@ -281,6 +281,19 @@ "outputs": [], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "setUpgraderRole", + "inputs": [ + { + "name": "newUpgraderRole", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "supportsInterface", @@ -451,6 +464,19 @@ ], "anonymous": false }, + { + "type": "event", + "name": "UpgraderRoleChanged", + "inputs": [ + { + "name": "newUpgraderRole", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, { "type": "error", "name": "AccessControlBadConfirmation", @@ -570,6 +596,11 @@ "name": "InvalidNewAdminAddress", "inputs": [] }, + { + "type": "error", + "name": "InvalidNewUpgraderRoleAddress", + "inputs": [] + }, { "type": "error", "name": "InvalidPoRepMarketAddress", diff --git a/src/ValidatorFactory.sol b/src/ValidatorFactory.sol index 8ae1872..c34ffcb 100644 --- a/src/ValidatorFactory.sol +++ b/src/ValidatorFactory.sol @@ -34,6 +34,7 @@ contract ValidatorFactory is UUPSUpgradeable, AccessControlUpgradeable { address _SPRegistry; address _beacon; address _admin; + address _upgraderRole; } // keccak256(abi.encode(uint256(keccak256("porepmarket.storage.ValidatorFactoryStorage")) - 1)) & ~bytes32(uint256(0xff)) @@ -68,6 +69,7 @@ contract ValidatorFactory is UUPSUpgradeable, AccessControlUpgradeable { error InvalidSPRegistryAddress(); error InvalidImplementationAddress(); error InvalidNewAdminAddress(); + error InvalidNewUpgraderRoleAddress(); error RoleManagementDisabled(); /** @@ -83,6 +85,12 @@ contract ValidatorFactory is UUPSUpgradeable, AccessControlUpgradeable { */ event AdminChanged(address indexed newAdmin); + /** + * @notice Emitted when the upgrader role is changed + * @param newUpgraderRole The address of the new upgrader role + */ + event UpgraderRoleChanged(address indexed newUpgraderRole); + /** * @notice Initializes the contract * @dev Initializes the contract by setting a default admin role and a UUPS upgradeable role @@ -104,6 +112,7 @@ contract ValidatorFactory is UUPSUpgradeable, AccessControlUpgradeable { ValidatorFactoryStorage storage $ = s(); $._beacon = address(new UpgradeableBeacon(implementation, admin)); $._admin = admin; + $._upgraderRole = admin; } /** @@ -200,6 +209,25 @@ contract ValidatorFactory is UUPSUpgradeable, AccessControlUpgradeable { emit AdminChanged(newAdmin); } + /** + * @notice Sets a new upgrader role for the contract + * @dev Only callable by the current admin. Reverts if the new upgrader role address is the zero address. + * @param newUpgraderRole The new upgrader role address + */ + function setUpgraderRole(address newUpgraderRole) external onlyRole(DEFAULT_ADMIN_ROLE) { + if (newUpgraderRole == address(0)) { + revert InvalidNewUpgraderRoleAddress(); + } + ValidatorFactoryStorage storage $ = s(); + + _revokeRole(UPGRADER_ROLE, $._upgraderRole); + _grantRole(UPGRADER_ROLE, newUpgraderRole); + + $._upgraderRole = newUpgraderRole; + + emit UpgraderRoleChanged(newUpgraderRole); + } + // solhint-disable use-natspec /** * @notice Disabled role management functions diff --git a/test/ValidatorFactory.t.sol b/test/ValidatorFactory.t.sol index b7c49c6..1b2c74a 100644 --- a/test/ValidatorFactory.t.sol +++ b/test/ValidatorFactory.t.sol @@ -266,4 +266,58 @@ contract ValidatorFactoryTest is Test { vm.expectRevert(abi.encodeWithSelector(ValidatorFactory.RoleManagementDisabled.selector)); factory.renounceRole(adminRole, admin); } + + function testSetUpgraderRoleRevertsWhenNewUpgraderRoleIsZero() public { + Validator validatorImplementation = new Validator(); + ValidatorFactory factoryImplementation = new ValidatorFactory(); + address validAdmin = address(0xABCD); + + factoryImplementation.initialize(validAdmin, address(validatorImplementation)); + + vm.prank(validAdmin); + vm.expectRevert(ValidatorFactory.InvalidNewUpgraderRoleAddress.selector); + factoryImplementation.setUpgraderRole(address(0)); + } + + function testSetsNewUpgraderRoleCorrectly() public { + Validator validatorImplementation = new Validator(); + ValidatorFactory factoryImplementation = new ValidatorFactory(); + address validAdmin = address(0xABCD); + address newUpgrader = address(0xDCBA); + + factoryImplementation.initialize(validAdmin, address(validatorImplementation)); + + vm.prank(validAdmin); + factoryImplementation.setUpgraderRole(newUpgrader); + + assertTrue(factoryImplementation.hasRole(factoryImplementation.UPGRADER_ROLE(), newUpgrader)); + } + + function testSetAdminEmitsAdminChangedEvent() public { + Validator validatorImplementation = new Validator(); + ValidatorFactory factoryImplementation = new ValidatorFactory(); + address validAdmin = address(0xABCD); + address newValidAdmin = address(0xDCBA); + + factoryImplementation.initialize(validAdmin, address(validatorImplementation)); + + vm.expectEmit(true, false, false, false); + emit ValidatorFactory.AdminChanged(newValidAdmin); + vm.prank(validAdmin); + factoryImplementation.setAdmin(newValidAdmin); + } + + function testSetUpgraderRoleEmitsUpgraderRoleChangedEvent() public { + Validator validatorImplementation = new Validator(); + ValidatorFactory factoryImplementation = new ValidatorFactory(); + address validAdmin = address(0xABCD); + address newUpgrader = address(0xDCBA); + + factoryImplementation.initialize(validAdmin, address(validatorImplementation)); + + vm.expectEmit(true, false, false, false); + emit ValidatorFactory.UpgraderRoleChanged(newUpgrader); + vm.prank(validAdmin); + factoryImplementation.setUpgraderRole(newUpgrader); + } }