-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Roles): new Roles contract + tests (#99)
* feat(Roles): new Roles contract + tests * style(scripts): prettier * chore(npm): version bump * feat: roles full test coverage & live network support * Update contracts/utils/Roles.sol Co-authored-by: Dean <[email protected]> * Update contracts/interfaces/IRoles.sol Co-authored-by: Dean <[email protected]> * Update contracts/interfaces/IRoles.sol Co-authored-by: Dean <[email protected]> * refactor(Roles): new RoleTransfer contract * docs(Roles): better NatSpec comments * refactor(Roles): renaming into RolesBase and Roles * refactor(Roles): renaming into RolesBase and Roles * refactor(Roles): moving hasRole view * feat: full test coverage * Apply suggestions from code review * Update contracts/utils/RolesBase.sol * Apply suggestions from code review * Update contracts/utils/RolesBase.sol Co-authored-by: Milap Sheth <[email protected]> * fix(Roles): always checking roles before transferring * fix(Roles): add/removeRole event * refactor(Roles): _addRole to reuse _addRoles * style(solidity): prettier --------- Co-authored-by: Dean Amiel <[email protected]> Co-authored-by: Dean <[email protected]> Co-authored-by: Milap Sheth <[email protected]>
- Loading branch information
1 parent
1ba9c07
commit 9ff8488
Showing
11 changed files
with
981 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { IRolesBase } from './IRolesBase.sol'; | ||
|
||
/** | ||
* @title IRoles Interface | ||
* @notice IRoles is an interface that abstracts the implementation of a | ||
* contract with role control features. It's commonly included for the functionality to | ||
* get current role, transfer role, and propose and accept role. | ||
*/ | ||
interface IRoles is IRolesBase { | ||
error InvalidProposedAccount(address account); | ||
|
||
/** | ||
* @notice Checks if an account has all the roles. | ||
* @param account The address to check | ||
* @param roles The roles to check | ||
* @return True if the account has all the roles, false otherwise | ||
*/ | ||
function hasAllTheRoles(address account, uint8[] memory roles) external view returns (bool); | ||
|
||
/** | ||
* @notice Checks if an account has any of the roles. | ||
* @param account The address to check | ||
* @param roles The roles to check | ||
* @return True if the account has any of the roles, false otherwise | ||
*/ | ||
function hasAnyOfRoles(address account, uint8[] memory roles) external view returns (bool); | ||
|
||
/** | ||
* @notice Returns the roles of an account. | ||
* @param account The address to get the roles for | ||
* @return accountRoles The roles of the account in uint256 format | ||
*/ | ||
function getAccountRoles(address account) external view returns (uint256 accountRoles); | ||
|
||
/** | ||
* @notice Returns the pending role of the contract. | ||
* @param fromAccount The address with the current roles | ||
* @param toAccount The address with the pending roles | ||
* @return proposedRoles_ The pending role of the contract in uint256 format | ||
*/ | ||
function getProposedRoles(address fromAccount, address toAccount) external view returns (uint256 proposedRoles_); | ||
|
||
/** | ||
* @notice Transfers roles of the contract to a new account. | ||
* @dev Can only be called by the account with all the roles. | ||
* @dev Emits RolesRemoved and RolesAdded events. | ||
* @param toAccount The address to transfer role to | ||
* @param roles The roles to transfer | ||
*/ | ||
function transferRoles(address toAccount, uint8[] memory roles) external; | ||
|
||
/** | ||
* @notice Propose to transfer roles of message sender to a new account. | ||
* @dev Can only be called by the account with all the proposed roles. | ||
* @dev emits a RolesProposed event. | ||
* @dev Roles are not transferred until the new role accepts the role transfer. | ||
* @param toAccount The address to transfer role to | ||
* @param roles The roles to transfer | ||
*/ | ||
function proposeRoles(address toAccount, uint8[] memory roles) external; | ||
|
||
/** | ||
* @notice Accepts roles transferred from another account. | ||
* @dev Can only be called by the pending account with all the proposed roles. | ||
* @dev Emits RolesRemoved and RolesAdded events. | ||
* @param fromAccount The address of the current role | ||
* @param roles The roles to accept | ||
*/ | ||
function acceptRoles(address fromAccount, uint8[] memory roles) external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* @title IRolesBase Interface | ||
* @notice IRolesBase is an interface that abstracts the implementation of a | ||
* contract with role control internal functions. | ||
*/ | ||
interface IRolesBase { | ||
error MissingRole(address account, uint8 role); | ||
error MissingAllRoles(address account, uint8[] roles); | ||
error MissingAnyOfRoles(address account, uint8[] roles); | ||
|
||
error InvalidProposedRoles(address fromAccount, address toAccount, uint8[] roles); | ||
|
||
event RolesProposed(address indexed fromAccount, address indexed toAccount, uint8[] roles); | ||
event RolesAdded(address indexed account, uint8[] roles); | ||
event RolesRemoved(address indexed account, uint8[] roles); | ||
|
||
/** | ||
* @notice Checks if an account has a role. | ||
* @param account The address to check | ||
* @param role The role to check | ||
* @return True if the account has the role, false otherwise | ||
*/ | ||
function hasRole(address account, uint8 role) external view returns (bool); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { Roles } from '../../utils/Roles.sol'; | ||
|
||
contract TestRoles is Roles { | ||
error InvalidRolesLength(); | ||
|
||
event NumSet(uint256 _num); | ||
|
||
uint256 public num; | ||
|
||
constructor(address[] memory accounts, uint8[][] memory roleSets) { | ||
uint256 length = accounts.length; | ||
if (length != roleSets.length) revert InvalidRolesLength(); | ||
|
||
for (uint256 i = 0; i < length; ++i) { | ||
_addRoles(accounts[i], roleSets[i]); | ||
} | ||
} | ||
|
||
function setNum(uint256 _num, uint8 role) external onlyRole(role) { | ||
num = _num; | ||
emit NumSet(_num); | ||
} | ||
|
||
function setNumWithAllRoles(uint256 _num, uint8[] calldata roles) external withEveryRole(roles) { | ||
num = _num; | ||
emit NumSet(_num); | ||
} | ||
|
||
function setNumWithAnyRoles(uint256 _num, uint8[] calldata roles) external withAnyRole(roles) { | ||
num = _num; | ||
emit NumSet(_num); | ||
} | ||
|
||
function addRole(address account, uint8 role) external { | ||
_addRole(account, role); | ||
} | ||
|
||
function removeRole(address account, uint8 role) external { | ||
_removeRole(account, role); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { IRoles } from '../interfaces/IRoles.sol'; | ||
import { RolesBase } from './RolesBase.sol'; | ||
|
||
/** | ||
* @title Roles | ||
* @notice A contract module which provides set of external functions providing basic role transferring functionality. | ||
* | ||
* @notice The role account is set through role transfer. This module makes | ||
* it possible to transfer the role of the contract to a new account in one | ||
* step, as well as to an interim pending role. In the second flow the role does not | ||
* change until the pending role accepts the role transfer. | ||
*/ | ||
contract Roles is RolesBase, IRoles { | ||
/** | ||
* @notice Checks if an account has all the roles. | ||
* @param account The address to check | ||
* @param roles The roles to check | ||
* @return True if the account has all the roles, false otherwise | ||
*/ | ||
function hasAllTheRoles(address account, uint8[] memory roles) public view returns (bool) { | ||
return _hasAllTheRoles(_getRoles(account), roles); | ||
} | ||
|
||
/** | ||
* @notice Checks if an account has any of the roles. | ||
* @param account The address to check | ||
* @param roles The roles to check | ||
* @return True if the account has any of the roles, false otherwise | ||
*/ | ||
function hasAnyOfRoles(address account, uint8[] memory roles) public view returns (bool) { | ||
return _hasAnyOfRoles(_getRoles(account), roles); | ||
} | ||
|
||
/** | ||
* @notice Returns the roles of an account. | ||
* @param account The address to get the roles for | ||
* @return accountRoles The roles of the account in uint256 format | ||
*/ | ||
function getAccountRoles(address account) public view returns (uint256 accountRoles) { | ||
accountRoles = _getRoles(account); | ||
} | ||
|
||
/** | ||
* @notice Returns the pending role of the contract. | ||
* @param fromAccount The address with the current roles | ||
* @param toAccount The address with the pending roles | ||
* @return proposedRoles_ The pending role of the contract in uint256 format | ||
*/ | ||
function getProposedRoles(address fromAccount, address toAccount) public view returns (uint256 proposedRoles_) { | ||
proposedRoles_ = _getProposedRoles(fromAccount, toAccount); | ||
} | ||
|
||
/** | ||
* @notice Propose to transfer roles of message sender to a new account. | ||
* @dev Can only be called by the account with all the proposed roles. | ||
* @dev emits a RolesProposed event. | ||
* @dev Roles are not transferred until the new role accepts the role transfer. | ||
* @param toAccount The address to transfer role to | ||
* @param roles The roles to transfer | ||
*/ | ||
function proposeRoles(address toAccount, uint8[] memory roles) external virtual { | ||
if (toAccount == address(0) || toAccount == msg.sender) revert InvalidProposedAccount(toAccount); | ||
|
||
_proposeRoles(msg.sender, toAccount, roles); | ||
} | ||
|
||
/** | ||
* @notice Accepts roles transferred from another account. | ||
* @dev Can only be called by the pending account with all the proposed roles. | ||
* @dev Emits RolesRemoved and RolesAdded events. | ||
* @param fromAccount The address of the current role | ||
* @param roles The roles to accept | ||
*/ | ||
function acceptRoles(address fromAccount, uint8[] memory roles) external virtual { | ||
_acceptRoles(fromAccount, msg.sender, roles); | ||
} | ||
|
||
/** | ||
* @notice Transfers roles of the contract to a new account. | ||
* @dev Can only be called by the account with all the roles. | ||
* @dev Emits RolesRemoved and RolesAdded events. | ||
* @param toAccount The address to transfer role to | ||
* @param roles The roles to transfer | ||
*/ | ||
function transferRoles(address toAccount, uint8[] memory roles) external virtual { | ||
if (toAccount == address(0) || toAccount == msg.sender) revert InvalidProposedAccount(toAccount); | ||
|
||
_transferRoles(msg.sender, toAccount, roles); | ||
} | ||
} |
Oops, something went wrong.