Skip to content
Merged
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
31 changes: 31 additions & 0 deletions abis/ValidatorFactory.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -570,6 +596,11 @@
"name": "InvalidNewAdminAddress",
"inputs": []
},
{
"type": "error",
"name": "InvalidNewUpgraderRoleAddress",
"inputs": []
},
{
"type": "error",
"name": "InvalidPoRepMarketAddress",
Expand Down
28 changes: 28 additions & 0 deletions src/ValidatorFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -68,6 +69,7 @@ contract ValidatorFactory is UUPSUpgradeable, AccessControlUpgradeable {
error InvalidSPRegistryAddress();
error InvalidImplementationAddress();
error InvalidNewAdminAddress();
error InvalidNewUpgraderRoleAddress();
error RoleManagementDisabled();

/**
Expand All @@ -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
Expand All @@ -104,6 +112,7 @@ contract ValidatorFactory is UUPSUpgradeable, AccessControlUpgradeable {
ValidatorFactoryStorage storage $ = s();
$._beacon = address(new UpgradeableBeacon(implementation, admin));
$._admin = admin;
$._upgraderRole = admin;
}

/**
Expand Down Expand Up @@ -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
Expand Down
54 changes: 54 additions & 0 deletions test/ValidatorFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Loading