Skip to content

Commit

Permalink
Add support for RBAC contract deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
ermyas committed May 13, 2024
1 parent 9295f9e commit 219823c
Show file tree
Hide file tree
Showing 8 changed files with 580 additions and 47 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ coverage.json
typechain
typechain-types
node.json
.idea/

# Hardhat files
cache
Expand Down
139 changes: 139 additions & 0 deletions contracts/deployer/AccessControlledDeployer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright Immutable Pty Ltd 2018 - 2024
// SPDX-License-Identifier: Apache 2.0
pragma solidity 0.8.19;

import {IDeployer} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IDeployer.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";

contract AccessControlledDeployer is AccessControlEnumerable, Pausable {
/// @notice Role identifier for those who can pause the deployer
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER");

/// @notice Role identifier for those who can unpause the deployer
bytes32 public constant UNPAUSER_ROLE = keccak256("UNPAUSER");

/// @notice Role identifier for those who can deploy contracts
bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER");

/**
* @notice Construct a new RBACDeployer contract
* @param admin The address to grant the DEFAULT_ADMIN_ROLE
* @param pauser The address to grant the PAUSER_ROLE
* @param unpauser The address to grant the UNPAUSER_ROLE
*/
constructor(address admin, address pauser, address unpauser) {
require(admin != address(0), "admin is the zero address");

Check failure on line 27 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Use Custom Errors instead of require statements
require(pauser != address(0), "pauser is the zero address");

Check failure on line 28 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Use Custom Errors instead of require statements
require(unpauser != address(0), "unpauser is the zero address");

Check failure on line 29 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Use Custom Errors instead of require statements

_setupRole(DEFAULT_ADMIN_ROLE, admin);
_setupRole(PAUSER_ROLE, pauser);
_setupRole(UNPAUSER_ROLE, unpauser);
}

/**
* @notice Deploys a contract using a deployment method defined by `deployer`
* @param deployer The create2 or create3 deployer contract that will deploy the contract
* @param bytecode The bytecode of the contract to be deployed
* @param salt A salt to influence the contract address
* @dev Only address with DEPLOYER_ROLE can call this function
* @dev The function can only be called if the contract is not in a paused state
* @return The address of the deployed contract
*/
function deploy(IDeployer deployer, bytes memory bytecode, bytes32 salt)

Check failure on line 45 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Replace IDeployer·deployer,·bytes·memory·bytecode,·bytes32·salt) with ⏎········IDeployer·deployer,⏎········bytes·memory·bytecode,⏎········bytes32·salt
external

Check failure on line 46 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Replace ········external⏎········payable⏎········whenNotPaused⏎········onlyRole(DEPLOYER_ROLE)⏎········returns·(address)⏎··· with ····)·external·payable·whenNotPaused·onlyRole(DEPLOYER_ROLE)·returns·(address)
payable
whenNotPaused
onlyRole(DEPLOYER_ROLE)
returns (address)
{
require(address(deployer) != address(0), "deployer contract is the zero address");

Check warning on line 52 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Error message for require is too long

Check failure on line 52 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Use Custom Errors instead of require statements
return deployer.deploy(bytecode, salt);
}

/**
* @notice Deploys a contract using a deployment method defined by `deployer` and initializes it
* @param deployer The create2 or create3 deployer contract that will deploy the contract
* @param bytecode The bytecode of the contract to be deployed
* @param salt A salt to influence the contract address
* @param init Init data used to initialize the deployed contract
* @dev Only address with DEPLOYER_ROLE can call this function
* @dev The function can only be called if the contract is not in a paused state
* @return The address of the deployed contract
*/
function deployAndInit(IDeployer deployer, bytes memory bytecode, bytes32 salt, bytes calldata init)

Check failure on line 66 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Replace IDeployer·deployer,·bytes·memory·bytecode,·bytes32·salt,·bytes·calldata·init) with ⏎········IDeployer·deployer,⏎········bytes·memory·bytecode,⏎········bytes32·salt,⏎········bytes·calldata·init
external

Check failure on line 67 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Replace ········external⏎········payable⏎········whenNotPaused⏎········onlyRole(DEPLOYER_ROLE)⏎········returns·(address)⏎··· with ····)·external·payable·whenNotPaused·onlyRole(DEPLOYER_ROLE)·returns·(address)
payable
whenNotPaused
onlyRole(DEPLOYER_ROLE)
returns (address)
{
require(address(deployer) != address(0), "deployer contract is the zero address");

Check warning on line 73 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Error message for require is too long

Check failure on line 73 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Use Custom Errors instead of require statements
return deployer.deployAndInit{value: msg.value}(bytecode, salt, init);
}

/**
* @notice Grants a list of addresses the DEPLOYER_ROLE
* @param deployers list of addresses to grant the DEPLOYER_ROLE
* @dev Only address with DEFAULT_ADMIN_ROLE can call this function
* @dev The function emits `RoleGranted` event for each address granted the DEPLOYER_ROLE.
* This is not emitted if an address is already a deployer
*/
function grantDeployerRole(address[] memory deployers) public {
require(deployers.length > 0, "deployers list is empty");

Check failure on line 85 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Use Custom Errors instead of require statements
for (uint256 i = 0; i < deployers.length; i++) {
require(deployers[i] != address(0), "deployer is the zero address");
grantRole(DEPLOYER_ROLE, deployers[i]);
}
}

/**
* @notice Revokes the DEPLOYER_ROLE for a list of addresses
* @param deployers list of addresses to revoke the DEPLOYER_ROLE from
* @dev Only address with DEFAULT_ADMIN_ROLE can call this function
* @dev The function emits `RoleRevoked` event for each address for which the DEPLOYER_ROLE was revoked
* This is not emitted if an address was not a deployer
*/
function revokeDeployerRole(address[] memory deployers) public {
require(deployers.length > 0, "deployers list is empty");
for (uint256 i = 0; i < deployers.length; i++) {
require(deployers[i] != address(0), "deployer is the zero address");
revokeRole(DEPLOYER_ROLE, deployers[i]);
}
}

/**
* @notice Transfers the ownership of `ownableDeployer` from this contract to `newOwner`
* @param ownableDeployer The create2 or create3 ownable deployer contract to change the owner of
* @param newOwner The new owner of the deployer contract
* @dev Only address with DEFAULT_ADMIN_ROLE can call this function
* @dev This function requires that the current owner of `ownableDeployer` is this contract
*/
function transferOwnershipOfDeployer(Ownable ownableDeployer, address newOwner)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
require(address(ownableDeployer) != address(0), "deployer contract is the zero address");

Check warning on line 118 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Error message for require is too long
require(newOwner != address(0), "new owner is the zero address");
require(ownableDeployer.owner() == address(this), "deployer contract is not owned by this contract");

Check warning on line 120 in contracts/deployer/AccessControlledDeployer.sol

View workflow job for this annotation

GitHub Actions / Run solhint

Error message for require is too long
ownableDeployer.transferOwnership(newOwner);
}

/**
* @notice Pause the contract, preventing any new deployments
* @dev Only PAUSER_ROLE can call this function
*/
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}

/**
* @notice Unpause the contract if it was paused, re-enabling new deployments
* @dev Only UNPAUSER_ROLE can call this function
*/
function unpause() external onlyRole(UNPAUSER_ROLE) {
_unpause();
}
}
1 change: 1 addition & 0 deletions contracts/deployer/create/OwnableCreateDeploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contract OwnableCreateDeploy {
* @param bytecode The bytecode of the contract to be deployed
*/
// slither-disable-next-line locked-ether

function deploy(bytes memory bytecode) external payable {
// solhint-disable-next-line custom-errors
require(msg.sender == owner, "CreateDeploy: caller is not the owner");
Expand Down
Loading

0 comments on commit 219823c

Please sign in to comment.