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

feat(Roles): new Roles contract + tests #99

Merged
merged 22 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
74 changes: 74 additions & 0 deletions contracts/interfaces/IRoles.sol
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;
}
28 changes: 28 additions & 0 deletions contracts/interfaces/IRolesBase.sol
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);
}
45 changes: 45 additions & 0 deletions contracts/test/utils/TestRoles.sol
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);
}
}
94 changes: 94 additions & 0 deletions contracts/utils/Roles.sol
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);
}
}
Loading
Loading