From d54f4ac4b703c770ce7c08a1854648ee309a2101 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 8 Sep 2023 01:58:50 +0200 Subject: [PATCH] Rename AccessManager groups to roles (#4580) --- contracts/access/manager/AccessManager.sol | 326 ++++++----- contracts/access/manager/IAccessManager.sol | 47 +- test/access/manager/AccessManager.test.js | 546 +++++++++--------- .../extensions/GovernorTimelockAccess.test.js | 18 +- 4 files changed, 461 insertions(+), 476 deletions(-) diff --git a/contracts/access/manager/AccessManager.sol b/contracts/access/manager/AccessManager.sol index 30e77f126..e397f5d44 100644 --- a/contracts/access/manager/AccessManager.sol +++ b/contracts/access/manager/AccessManager.sol @@ -15,21 +15,21 @@ import {Time} from "../../utils/types/Time.sol"; * * The smart contracts under the control of an AccessManager instance will have a set of "restricted" functions, and the * exact details of how access is restricted for each of those functions is configurable by the admins of the instance. - * These restrictions are expressed in terms of "groups". + * These restrictions are expressed in terms of "roles". * - * An AccessManager instance will define a set of groups. Accounts can be added into any number of these groups. Each of + * An AccessManager instance will define a set of roles. Accounts can be added into any number of these roles. Each of * them defines a role, and may confer access to some of the restricted functions in the system, as configured by admins - * through the use of {setFunctionAllowedGroup}. + * through the use of {setFunctionAllowedRoles}. * * Note that a function in a target contract may become permissioned in this way only when: 1) said contract is * {AccessManaged} and is connected to this contract as its manager, and 2) said function is decorated with the * `restricted` modifier. * - * There is a special group defined by default named "public" which all accounts automatically have. + * There is a special role defined by default named "public" which all accounts automatically have. * - * Contracts where functions are mapped to groups are said to be in a "custom" mode, but contracts can also be - * configured in two special modes: 1) the "open" mode, where all functions are allowed to the "public" group, and 2) - * the "closed" mode, where no function is allowed to any group. + * Contracts where functions are mapped to roles are said to be in a "custom" mode, but contracts can also be + * configured in two special modes: 1) the "open" mode, where all functions are allowed to the "public" role, and 2) + * the "closed" mode, where no function is allowed to any role. * * Since all the permissions of the managed system can be modified by the admins of this instance, it is expected that * they will be highly secured (e.g., a multisig or a well-configured DAO). @@ -52,27 +52,26 @@ contract AccessManager is Context, Multicall, IAccessManager { using Time for *; struct TargetConfig { - mapping(bytes4 selector => uint64 groupId) allowedGroups; + mapping(bytes4 selector => uint64 roleId) allowedRoles; Time.Delay adminDelay; bool closed; } - // Structure that stores the details for a group/account pair. This structure fits into a single slot. + // Structure that stores the details for a role/account pair. This structures fit into a single slot. struct Access { - // Timepoint at which the user gets the permission. If this is either 0, or in the future, the group permission + // Timepoint at which the user gets the permission. If this is either 0, or in the future, the role permission // is not available. uint48 since; - // delay for execution. Only applies to restricted() / execute() calls. This does not restrict access to - // functions that use the `onlyGroup` modifier. + // delay for execution. Only applies to restricted() / execute() calls. Time.Delay delay; } - // Structure that stores the details of a group, including: - // - the members of the group - // - the admin group (that can grant or revoke permissions) - // - the guardian group (that can cancel operations targeting functions that need this group - // - the grant delay - struct Group { + // Structure that stores the details of a role, including: + // - the members of the role + // - the admin role (that can grant or revoke permissions) + // - the guardian role (that can cancel operations targeting functions that need this role) + // - the grand delay + struct Role { mapping(address user => Access access) members; uint64 admin; uint64 guardian; @@ -84,11 +83,11 @@ contract AccessManager is Context, Multicall, IAccessManager { uint32 nonce; } - uint64 public constant ADMIN_GROUP = type(uint64).min; // 0 - uint64 public constant PUBLIC_GROUP = type(uint64).max; // 2**64-1 + uint64 public constant ADMIN_ROLE = type(uint64).min; // 0 + uint64 public constant PUBLIC_ROLE = type(uint64).max; // 2**64-1 mapping(address target => TargetConfig mode) private _targets; - mapping(uint64 groupId => Group) private _groups; + mapping(uint64 roleId => Role) private _roles; mapping(bytes32 operationId => Schedule) private _schedules; // This should be transient storage when supported by the EVM. @@ -109,7 +108,7 @@ contract AccessManager is Context, Multicall, IAccessManager { } // admin is active immediately and without any execution delay. - _grantGroup(ADMIN_GROUP, initialAdmin, 0, 0); + _grantRole(ADMIN_ROLE, initialAdmin, 0, 0); } // =================================================== GETTERS ==================================================== @@ -137,9 +136,9 @@ contract AccessManager is Context, Multicall, IAccessManager { // permissions. We verify that the call "identifier", which is set during {execute}, is correct. return (_executionId == _hashExecutionId(target, selector), 0); } else { - uint64 groupId = getTargetFunctionGroup(target, selector); - (bool inGroup, uint32 currentDelay) = hasGroup(groupId, caller); - return inGroup ? (currentDelay == 0, currentDelay) : (false, 0); + uint64 roleId = getTargetFunctionRole(target, selector); + (bool isMember, uint32 currentDelay) = hasRole(roleId, caller); + return isMember ? (currentDelay == 0, currentDelay) : (false, 0); } } @@ -153,7 +152,7 @@ contract AccessManager is Context, Multicall, IAccessManager { /** * @dev Minimum setback for all delay updates, with the exception of execution delays, which * can be increased without setback (and in the event of an accidental increase can be reset - * via {revokeGroup}). Defaults to 5 days. + * via {revokeRole}). Defaults to 5 days. */ function minSetback() public view virtual returns (uint32) { return 5 days; @@ -167,11 +166,11 @@ contract AccessManager is Context, Multicall, IAccessManager { } /** - * @dev Get the permission level (group) required to call a function. This only applies for contract that are + * @dev Get the permission level (role) required to call a function. This only applies for contract that are * operating under the `Custom` mode. */ - function getTargetFunctionGroup(address target, bytes4 selector) public view virtual returns (uint64) { - return _targets[target].allowedGroups[selector]; + function getTargetFunctionRole(address target, bytes4 selector) public view virtual returns (uint64) { + return _targets[target].allowedRoles[selector]; } function getTargetAdminDelay(address target) public view virtual returns (uint32) { @@ -179,35 +178,35 @@ contract AccessManager is Context, Multicall, IAccessManager { } /** - * @dev Get the id of the group that acts as an admin for given group. + * @dev Get the id of the role that acts as an admin for given role. * - * The admin permission is required to grant the group, revoke the group and update the execution delay to execute - * an operation that is restricted to this group. + * The admin permission is required to grant the role, revoke the role and update the execution delay to execute + * an operation that is restricted to this role. */ - function getGroupAdmin(uint64 groupId) public view virtual returns (uint64) { - return _groups[groupId].admin; + function getRoleAdmin(uint64 roleId) public view virtual returns (uint64) { + return _roles[roleId].admin; } /** - * @dev Get the group that acts as a guardian for a given group. + * @dev Get the role that acts as a guardian for a given role. * - * The guardian permission allows canceling operations that have been scheduled under the group. + * The guardian permission allows canceling operations that have been scheduled under the role. */ - function getGroupGuardian(uint64 groupId) public view virtual returns (uint64) { - return _groups[groupId].guardian; + function getRoleGuardian(uint64 roleId) public view virtual returns (uint64) { + return _roles[roleId].guardian; } /** - * @dev Get the group current grant delay, that value may change at any point, without an event emitted, following + * @dev Get the role current grant delay, that value may change at any point, without an event emitted, following * a call to {setGrantDelay}. Changes to this value, including effect timepoint are notified by the - * {GroupGrantDelayChanged} event. + * {RoleGrantDelayChanged} event. */ - function getGroupGrantDelay(uint64 groupId) public view virtual returns (uint32) { - return _groups[groupId].grantDelay.get(); + function getRoleGrantDelay(uint64 roleId) public view virtual returns (uint32) { + return _roles[roleId].grantDelay.get(); } /** - * @dev Get the access details for a given account in a given group. These details include the timepoint at which + * @dev Get the access details for a given account in a given role. These details include the timepoint at which * membership becomes active, and the delay applied to all operation by this user that requires this permission * level. * @@ -217,8 +216,8 @@ contract AccessManager is Context, Multicall, IAccessManager { * [2] Pending execution delay for the account. * [3] Timestamp at which the pending execution delay will become active. 0 means no delay update is scheduled. */ - function getAccess(uint64 groupId, address account) public view virtual returns (uint48, uint32, uint32, uint48) { - Access storage access = _groups[groupId].members[account]; + function getAccess(uint64 roleId, address account) public view virtual returns (uint48, uint32, uint32, uint48) { + Access storage access = _roles[roleId].members[account]; uint48 since = access.since; (uint32 currentDelay, uint32 pendingDelay, uint48 effect) = access.delay.getFull(); @@ -227,255 +226,254 @@ contract AccessManager is Context, Multicall, IAccessManager { } /** - * @dev Check if a given account currently had the permission level corresponding to a given group. Note that this + * @dev Check if a given account currently had the permission level corresponding to a given role. Note that this * permission might be associated with a delay. {getAccess} can provide more details. */ - function hasGroup(uint64 groupId, address account) public view virtual returns (bool, uint32) { - if (groupId == PUBLIC_GROUP) { + function hasRole(uint64 roleId, address account) public view virtual returns (bool, uint32) { + if (roleId == PUBLIC_ROLE) { return (true, 0); } else { - (uint48 inGroupSince, uint32 currentDelay, , ) = getAccess(groupId, account); - return (inGroupSince != 0 && inGroupSince <= Time.timestamp(), currentDelay); + (uint48 hasRoleSince, uint32 currentDelay, , ) = getAccess(roleId, account); + return (hasRoleSince != 0 && hasRoleSince <= Time.timestamp(), currentDelay); } } - // =============================================== GROUP MANAGEMENT =============================================== + // =============================================== ROLE MANAGEMENT =============================================== /** - * @dev Give a label to a group, for improved group discoverabily by UIs. + * @dev Give a label to a role, for improved role discoverabily by UIs. * - * Emits a {GroupLabel} event. + * Emits a {RoleLabel} event. */ - function labelGroup(uint64 groupId, string calldata label) public virtual onlyAuthorized { - if (groupId == ADMIN_GROUP || groupId == PUBLIC_GROUP) { - revert AccessManagerLockedGroup(groupId); + function labelRole(uint64 roleId, string calldata label) public virtual onlyAuthorized { + if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) { + revert AccessManagerLockedRole(roleId); } - emit GroupLabel(groupId, label); + emit RoleLabel(roleId, label); } /** - * @dev Add `account` to `groupId`, or change its execution delay. + * @dev Add `account` to `roleId`, or change its execution delay. * - * This gives the account the authorization to call any function that is restricted to this group. An optional + * This gives the account the authorization to call any function that is restricted to this role. An optional * execution delay (in seconds) can be set. If that delay is non 0, the user is required to schedule any operation - * that is restricted to members this group. The user will only be able to execute the operation after the delay has + * that is restricted to members this role. The user will only be able to execute the operation after the delay has * passed, before it has expired. During this period, admin and guardians can cancel the operation (see {cancel}). * - * If the account has already been granted this group, the execution delay will be updated. This update is not + * If the account has already been granted this role, the execution delay will be updated. This update is not * immediate and follows the delay rules. For example, If a user currently has a delay of 3 hours, and this is * called to reduce that delay to 1 hour, the new delay will take some time to take effect, enforcing that any * operation executed in the 3 hours that follows this update was indeed scheduled before this update. * * Requirements: * - * - the caller must be in the group's admins + * - the caller must be in the role's admins * - * Emits a {GroupGranted} event + * Emits a {RoleGranted} event */ - function grantGroup(uint64 groupId, address account, uint32 executionDelay) public virtual onlyAuthorized { - _grantGroup(groupId, account, getGroupGrantDelay(groupId), executionDelay); + function grantRole(uint64 roleId, address account, uint32 executionDelay) public virtual onlyAuthorized { + _grantRole(roleId, account, getRoleGrantDelay(roleId), executionDelay); } /** - * @dev Remove an account for a group, with immediate effect. If the sender is not in the group, this call has no + * @dev Remove an account for a role, with immediate effect. If the sender is not in the role, this call has no * effect. * * Requirements: * - * - the caller must be in the group's admins + * - the caller must be in the role's admins * - * Emits a {GroupRevoked} event + * Emits a {RoleRevoked} event */ - function revokeGroup(uint64 groupId, address account) public virtual onlyAuthorized { - _revokeGroup(groupId, account); + function revokeRole(uint64 roleId, address account) public virtual onlyAuthorized { + _revokeRole(roleId, account); } /** - * @dev Renounce group permissions for the calling account, with immediate effect. If the sender is not in - * the group, this call has no effect. + * @dev Renounce role permissions for the calling account, with immediate effect. If the sender is not in + * the role, this call has no effect. * * Requirements: * * - the caller must be `callerConfirmation`. * - * Emits a {GroupRevoked} event + * Emits a {RoleRevoked} event */ - function renounceGroup(uint64 groupId, address callerConfirmation) public virtual { + function renounceRole(uint64 roleId, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessManagerBadConfirmation(); } - _revokeGroup(groupId, callerConfirmation); + _revokeRole(roleId, callerConfirmation); } /** - * @dev Change admin group for a given group. + * @dev Change admin role for a given role. * * Requirements: * * - the caller must be a global admin * - * Emits a {GroupAdminChanged} event + * Emits a {RoleAdminChanged} event */ - function setGroupAdmin(uint64 groupId, uint64 admin) public virtual onlyAuthorized { - _setGroupAdmin(groupId, admin); + function setRoleAdmin(uint64 roleId, uint64 admin) public virtual onlyAuthorized { + _setRoleAdmin(roleId, admin); } /** - * @dev Change guardian group for a given group. + * @dev Change guardian role for a given role. * * Requirements: * * - the caller must be a global admin * - * Emits a {GroupGuardianChanged} event + * Emits a {RoleGuardianChanged} event */ - function setGroupGuardian(uint64 groupId, uint64 guardian) public virtual onlyAuthorized { - _setGroupGuardian(groupId, guardian); + function setRoleGuardian(uint64 roleId, uint64 guardian) public virtual onlyAuthorized { + _setRoleGuardian(roleId, guardian); } /** - * @dev Update the delay for granting a `groupId`. + * @dev Update the delay for granting a `roleId`. * * Requirements: * * - the caller must be a global admin * - * Emits a {GroupGrantDelayChanged} event + * Emits a {RoleGrantDelayChanged} event */ - function setGrantDelay(uint64 groupId, uint32 newDelay) public virtual onlyAuthorized { - _setGrantDelay(groupId, newDelay); + function setGrantDelay(uint64 roleId, uint32 newDelay) public virtual onlyAuthorized { + _setGrantDelay(roleId, newDelay); } /** - * @dev Internal version of {grantGroup} without access control. Returns true if the group was newly granted. + * @dev Internal version of {grantRole} without access control. Returns true if the role was newly granted. * - * Emits a {GroupGranted} event + * Emits a {RoleGranted} event */ - function _grantGroup( - uint64 groupId, + function _grantRole( + uint64 roleId, address account, uint32 grantDelay, uint32 executionDelay ) internal virtual returns (bool) { - if (groupId == PUBLIC_GROUP) { - revert AccessManagerLockedGroup(groupId); + if (roleId == PUBLIC_ROLE) { + revert AccessManagerLockedRole(roleId); } - bool inGroup = _groups[groupId].members[account].since != 0; - + bool newMember = _roles[roleId].members[account].since == 0; uint48 since; - if (inGroup) { + if (newMember) { + since = Time.timestamp() + grantDelay; + _roles[roleId].members[account] = Access({since: since, delay: executionDelay.toDelay()}); + } else { // No setback here. Value can be reset by doing revoke + grant, effectively allowing the admin to perform - // any change to the execution delay within the duration of the group admin delay. - (_groups[groupId].members[account].delay, since) = _groups[groupId].members[account].delay.withUpdate( + // any change to the execution delay within the duration of the role admin delay. + (_roles[roleId].members[account].delay, since) = _roles[roleId].members[account].delay.withUpdate( executionDelay, 0 ); - } else { - since = Time.timestamp() + grantDelay; - _groups[groupId].members[account] = Access({since: since, delay: executionDelay.toDelay()}); } - emit GroupGranted(groupId, account, executionDelay, since, !inGroup); - return !inGroup; + emit RoleGranted(roleId, account, executionDelay, since, newMember); + return newMember; } /** - * @dev Internal version of {revokeGroup} without access control. This logic is also used by {renounceGroup}. - * Returns true if the group was previously granted. + * @dev Internal version of {revokeRole} without access control. This logic is also used by {renounceRole}. + * Returns true if the role was previously granted. * - * Emits a {GroupRevoked} event + * Emits a {RoleRevoked} event */ - function _revokeGroup(uint64 groupId, address account) internal virtual returns (bool) { - if (groupId == PUBLIC_GROUP) { - revert AccessManagerLockedGroup(groupId); + function _revokeRole(uint64 roleId, address account) internal virtual returns (bool) { + if (roleId == PUBLIC_ROLE) { + revert AccessManagerLockedRole(roleId); } - if (_groups[groupId].members[account].since == 0) { + if (_roles[roleId].members[account].since == 0) { return false; } - delete _groups[groupId].members[account]; + delete _roles[roleId].members[account]; - emit GroupRevoked(groupId, account); + emit RoleRevoked(roleId, account); return true; } /** - * @dev Internal version of {setGroupAdmin} without access control. + * @dev Internal version of {setRoleAdmin} without access control. * - * Emits a {GroupAdminChanged} event + * Emits a {RoleAdminChanged} event */ - function _setGroupAdmin(uint64 groupId, uint64 admin) internal virtual { - if (groupId == ADMIN_GROUP || groupId == PUBLIC_GROUP) { - revert AccessManagerLockedGroup(groupId); + function _setRoleAdmin(uint64 roleId, uint64 admin) internal virtual { + if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) { + revert AccessManagerLockedRole(roleId); } - _groups[groupId].admin = admin; + _roles[roleId].admin = admin; - emit GroupAdminChanged(groupId, admin); + emit RoleAdminChanged(roleId, admin); } /** - * @dev Internal version of {setGroupGuardian} without access control. + * @dev Internal version of {setRoleGuardian} without access control. * - * Emits a {GroupGuardianChanged} event + * Emits a {RoleGuardianChanged} event */ - function _setGroupGuardian(uint64 groupId, uint64 guardian) internal virtual { - if (groupId == ADMIN_GROUP || groupId == PUBLIC_GROUP) { - revert AccessManagerLockedGroup(groupId); + function _setRoleGuardian(uint64 roleId, uint64 guardian) internal virtual { + if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) { + revert AccessManagerLockedRole(roleId); } - _groups[groupId].guardian = guardian; + _roles[roleId].guardian = guardian; - emit GroupGuardianChanged(groupId, guardian); + emit RoleGuardianChanged(roleId, guardian); } /** * @dev Internal version of {setGrantDelay} without access control. * - * Emits a {GroupGrantDelayChanged} event + * Emits a {RoleGrantDelayChanged} event */ - function _setGrantDelay(uint64 groupId, uint32 newDelay) internal virtual { - if (groupId == PUBLIC_GROUP) { - revert AccessManagerLockedGroup(groupId); + function _setGrantDelay(uint64 roleId, uint32 newDelay) internal virtual { + if (roleId == PUBLIC_ROLE) { + revert AccessManagerLockedRole(roleId); } uint48 effect; - (_groups[groupId].grantDelay, effect) = _groups[groupId].grantDelay.withUpdate(newDelay, minSetback()); + (_roles[roleId].grantDelay, effect) = _roles[roleId].grantDelay.withUpdate(newDelay, minSetback()); - emit GroupGrantDelayChanged(groupId, newDelay, effect); + emit RoleGrantDelayChanged(roleId, newDelay, effect); } // ============================================= FUNCTION MANAGEMENT ============================================== /** - * @dev Set the level of permission (`group`) required to call functions identified by the `selectors` in the + * @dev Set the level of permission (`role`) required to call functions identified by the `selectors` in the * `target` contract. * * Requirements: * * - the caller must be a global admin * - * Emits a {FunctionAllowedGroupUpdated} event per selector + * Emits a {FunctionAllowedRoleUpdated} event per selector */ - function setTargetFunctionGroup( + function setTargetFunctionRole( address target, bytes4[] calldata selectors, - uint64 groupId + uint64 roleId ) public virtual onlyAuthorized { for (uint256 i = 0; i < selectors.length; ++i) { - _setTargetFunctionGroup(target, selectors[i], groupId); + _setTargetFunctionRole(target, selectors[i], roleId); } } /** - * @dev Internal version of {setFunctionAllowedGroup} without access control. + * @dev Internal version of {setFunctionAllowedRole} without access control. * - * Emits a {FunctionAllowedGroupUpdated} event + * Emits a {FunctionAllowedRoleUpdated} event */ - function _setTargetFunctionGroup(address target, bytes4 selector, uint64 groupId) internal virtual { - _targets[target].allowedGroups[selector] = groupId; - emit TargetFunctionGroupUpdated(target, selector, groupId); + function _setTargetFunctionRole(address target, bytes4 selector, uint64 roleId) internal virtual { + _targets[target].allowedRoles[selector] = roleId; + emit TargetFunctionRoleUpdated(target, selector, roleId); } /** @@ -485,7 +483,7 @@ contract AccessManager is Context, Multicall, IAccessManager { * * - the caller must be a global admin * - * Emits a {FunctionAllowedGroupUpdated} event per selector + * Emits a {FunctionAllowedRoleUpdated} event per selector */ function setTargetAdminDelay(address target, uint32 newDelay) public virtual onlyAuthorized { _setTargetAdminDelay(target, newDelay); @@ -702,9 +700,9 @@ contract AccessManager is Context, Multicall, IAccessManager { if (_schedules[operationId].timepoint == 0) { revert AccessManagerNotScheduled(operationId); } else if (caller != msgsender) { - // calls can only be canceled by the account that scheduled them, a global admin, or by a guardian of the required group. - (bool isAdmin, ) = hasGroup(ADMIN_GROUP, msgsender); - (bool isGuardian, ) = hasGroup(getGroupGuardian(getTargetFunctionGroup(target, selector)), msgsender); + // calls can only be canceled by the account that scheduled them, a global admin, or by a guardian of the required role. + (bool isAdmin, ) = hasRole(ADMIN_ROLE, msgsender); + (bool isGuardian, ) = hasRole(getRoleGuardian(getTargetFunctionRole(target, selector)), msgsender); if (!isAdmin && !isGuardian) { revert AccessManagerUnauthorizedCancel(msgsender, caller, target, selector); } @@ -752,8 +750,8 @@ contract AccessManager is Context, Multicall, IAccessManager { (bool immediate, uint32 delay) = _canCallExtended(caller, address(this), _msgData()); if (!immediate) { if (delay == 0) { - (, uint64 requiredGroup, ) = _getAdminRestrictions(_msgData()); - revert AccessManagerUnauthorizedAccount(caller, requiredGroup); + (, uint64 requiredRole, ) = _getAdminRestrictions(_msgData()); + revert AccessManagerUnauthorizedAccount(caller, requiredRole); } else { _consumeScheduledOp(_hashOperation(caller, address(this), _msgData())); } @@ -765,7 +763,7 @@ contract AccessManager is Context, Multicall, IAccessManager { * * Returns: * - bool restricted: does this data match a restricted operation - * - uint64: which group is this operation restricted to + * - uint64: which role is this operation restricted to * - uint32: minimum delay to enforce for that operation (on top of the admin's execution delay) */ function _getAdminRestrictions(bytes calldata data) private view returns (bool, uint64, uint32) { @@ -777,33 +775,33 @@ contract AccessManager is Context, Multicall, IAccessManager { // Restricted to ADMIN with no delay beside any execution delay the caller may have if ( - selector == this.labelGroup.selector || - selector == this.setGroupAdmin.selector || - selector == this.setGroupGuardian.selector || + selector == this.labelRole.selector || + selector == this.setRoleAdmin.selector || + selector == this.setRoleGuardian.selector || selector == this.setGrantDelay.selector || selector == this.setTargetAdminDelay.selector ) { - return (true, ADMIN_GROUP, 0); + return (true, ADMIN_ROLE, 0); } // Restricted to ADMIN with the admin delay corresponding to the target if ( selector == this.updateAuthority.selector || selector == this.setTargetClosed.selector || - selector == this.setTargetFunctionGroup.selector + selector == this.setTargetFunctionRole.selector ) { // First argument is a target. address target = abi.decode(data[0x04:0x24], (address)); uint32 delay = getTargetAdminDelay(target); - return (true, ADMIN_GROUP, delay); + return (true, ADMIN_ROLE, delay); } - // Restricted to that group's admin with no delay beside any execution delay the caller may have. - if (selector == this.grantGroup.selector || selector == this.revokeGroup.selector) { - // First argument is a groupId. - uint64 groupId = abi.decode(data[0x04:0x24], (uint64)); - uint64 groupAdminId = getGroupAdmin(groupId); - return (true, groupAdminId, 0); + // Restricted to that role's admin with no delay beside any execution delay the caller may have. + if (selector == this.grantRole.selector || selector == this.revokeRole.selector) { + // First argument is a roleId. + uint64 roleId = abi.decode(data[0x04:0x24], (uint64)); + uint64 roleAdminId = getRoleAdmin(roleId); + return (true, roleAdminId, 0); } return (false, 0, 0); @@ -815,13 +813,13 @@ contract AccessManager is Context, Multicall, IAccessManager { */ function _canCallExtended(address caller, address target, bytes calldata data) private view returns (bool, uint32) { if (target == address(this)) { - (bool enabled, uint64 groupId, uint32 operationDelay) = _getAdminRestrictions(data); + (bool enabled, uint64 roleId, uint32 operationDelay) = _getAdminRestrictions(data); if (!enabled) { return (false, 0); } - (bool inGroup, uint32 executionDelay) = hasGroup(groupId, caller); - if (!inGroup) { + (bool inRole, uint32 executionDelay) = hasRole(roleId, caller); + if (!inRole) { return (false, 0); } diff --git a/contracts/access/manager/IAccessManager.sol b/contracts/access/manager/IAccessManager.sol index af6b2f3f0..b0c9a51e4 100644 --- a/contracts/access/manager/IAccessManager.sol +++ b/contracts/access/manager/IAccessManager.sol @@ -28,15 +28,14 @@ interface IAccessManager { */ event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce); - event GroupLabel(uint64 indexed groupId, string label); - event GroupGranted(uint64 indexed groupId, address indexed account, uint32 delay, uint48 since, bool newMember); - event GroupRevoked(uint64 indexed groupId, address indexed account); - event GroupAdminChanged(uint64 indexed groupId, uint64 indexed admin); - event GroupGuardianChanged(uint64 indexed groupId, uint64 indexed guardian); - event GroupGrantDelayChanged(uint64 indexed groupId, uint32 delay, uint48 since); - + event RoleLabel(uint64 indexed roleId, string label); + event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember); + event RoleRevoked(uint64 indexed roleId, address indexed account); + event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin); + event RoleGuardianChanged(uint64 indexed roleId, uint64 indexed guardian); + event RoleGrantDelayChanged(uint64 indexed roleId, uint32 delay, uint48 since); event TargetClosed(address indexed target, bool closed); - event TargetFunctionGroupUpdated(address indexed target, bytes4 selector, uint64 indexed groupId); + event TargetFunctionRoleUpdated(address indexed target, bytes4 selector, uint64 indexed roleId); event TargetAdminDelayUpdated(address indexed target, uint32 delay, uint48 since); error AccessManagerAlreadyScheduled(bytes32 operationId); @@ -44,9 +43,9 @@ interface IAccessManager { error AccessManagerNotReady(bytes32 operationId); error AccessManagerExpired(bytes32 operationId); error AccessManagerLockedAccount(address account); - error AccessManagerLockedGroup(uint64 groupId); + error AccessManagerLockedRole(uint64 roleId); error AccessManagerBadConfirmation(); - error AccessManagerUnauthorizedAccount(address msgsender, uint64 groupId); + error AccessManagerUnauthorizedAccount(address msgsender, uint64 roleId); error AccessManagerUnauthorizedCall(address caller, address target, bytes4 selector); error AccessManagerUnauthorizedConsume(address target); error AccessManagerUnauthorizedCancel(address msgsender, address caller, address target, bytes4 selector); @@ -62,35 +61,35 @@ interface IAccessManager { function isTargetClosed(address target) external view returns (bool); - function getTargetFunctionGroup(address target, bytes4 selector) external view returns (uint64); + function getTargetFunctionRole(address target, bytes4 selector) external view returns (uint64); function getTargetAdminDelay(address target) external view returns (uint32); - function getGroupAdmin(uint64 groupId) external view returns (uint64); + function getRoleAdmin(uint64 roleId) external view returns (uint64); - function getGroupGuardian(uint64 groupId) external view returns (uint64); + function getRoleGuardian(uint64 roleId) external view returns (uint64); - function getGroupGrantDelay(uint64 groupId) external view returns (uint32); + function getRoleGrantDelay(uint64 roleId) external view returns (uint32); - function getAccess(uint64 groupId, address account) external view returns (uint48, uint32, uint32, uint48); + function getAccess(uint64 roleId, address account) external view returns (uint48, uint32, uint32, uint48); - function hasGroup(uint64 groupId, address account) external view returns (bool, uint32); + function hasRole(uint64 roleId, address account) external view returns (bool, uint32); - function labelGroup(uint64 groupId, string calldata label) external; + function labelRole(uint64 roleId, string calldata label) external; - function grantGroup(uint64 groupId, address account, uint32 executionDelay) external; + function grantRole(uint64 roleId, address account, uint32 executionDelay) external; - function revokeGroup(uint64 groupId, address account) external; + function revokeRole(uint64 roleId, address account) external; - function renounceGroup(uint64 groupId, address callerConfirmation) external; + function renounceRole(uint64 roleId, address callerConfirmation) external; - function setGroupAdmin(uint64 groupId, uint64 admin) external; + function setRoleAdmin(uint64 roleId, uint64 admin) external; - function setGroupGuardian(uint64 groupId, uint64 guardian) external; + function setRoleGuardian(uint64 roleId, uint64 guardian) external; - function setGrantDelay(uint64 groupId, uint32 newDelay) external; + function setGrantDelay(uint64 roleId, uint32 newDelay) external; - function setTargetFunctionGroup(address target, bytes4[] calldata selectors, uint64 groupId) external; + function setTargetFunctionRole(address target, bytes4[] calldata selectors, uint64 roleId) external; function setTargetAdminDelay(address target, uint32 newDelay) external; diff --git a/test/access/manager/AccessManager.test.js b/test/access/manager/AccessManager.test.js index 92ae830b5..84fb8df21 100644 --- a/test/access/manager/AccessManager.test.js +++ b/test/access/manager/AccessManager.test.js @@ -11,17 +11,16 @@ const Ownable = artifacts.require('$Ownable'); const MAX_UINT64 = web3.utils.toBN((2n ** 64n - 1n).toString()); -const GROUPS = { +const ROLES = { ADMIN: web3.utils.toBN(0), SOME_ADMIN: web3.utils.toBN(17), SOME: web3.utils.toBN(42), PUBLIC: MAX_UINT64, }; -Object.assign(GROUPS, Object.fromEntries(Object.entries(GROUPS).map(([key, value]) => [value, key]))); +Object.assign(ROLES, Object.fromEntries(Object.entries(ROLES).map(([key, value]) => [value, key]))); const executeDelay = web3.utils.toBN(10); const grantDelay = web3.utils.toBN(10); - const MINSETBACK = time.duration.days(5); const formatAccess = access => [access[0], access[1].toString()]; @@ -32,11 +31,11 @@ contract('AccessManager', function (accounts) { beforeEach(async function () { this.manager = await AccessManager.new(admin); - // add member to group - await this.manager.$_setGroupAdmin(GROUPS.SOME, GROUPS.SOME_ADMIN); - await this.manager.$_setGroupGuardian(GROUPS.SOME, GROUPS.SOME_ADMIN); - await this.manager.$_grantGroup(GROUPS.SOME_ADMIN, manager, 0, 0); - await this.manager.$_grantGroup(GROUPS.SOME, member, 0, 0); + // add member to role + await this.manager.$_setRoleAdmin(ROLES.SOME, ROLES.SOME_ADMIN); + await this.manager.$_setRoleGuardian(ROLES.SOME, ROLES.SOME_ADMIN); + await this.manager.$_grantRole(ROLES.SOME_ADMIN, manager, 0, 0); + await this.manager.$_grantRole(ROLES.SOME, member, 0, 0); }); it('rejects zero address for initialAdmin', async function () { @@ -49,165 +48,165 @@ contract('AccessManager', function (accounts) { expect(await this.manager.minSetback()).to.be.bignumber.equal(MINSETBACK); }); - it('groups are correctly initialized', async function () { - // group admin - expect(await this.manager.getGroupAdmin(GROUPS.ADMIN)).to.be.bignumber.equal(GROUPS.ADMIN); - expect(await this.manager.getGroupAdmin(GROUPS.SOME_ADMIN)).to.be.bignumber.equal(GROUPS.ADMIN); - expect(await this.manager.getGroupAdmin(GROUPS.SOME)).to.be.bignumber.equal(GROUPS.SOME_ADMIN); - expect(await this.manager.getGroupAdmin(GROUPS.PUBLIC)).to.be.bignumber.equal(GROUPS.ADMIN); - // group guardian - expect(await this.manager.getGroupGuardian(GROUPS.ADMIN)).to.be.bignumber.equal(GROUPS.ADMIN); - expect(await this.manager.getGroupGuardian(GROUPS.SOME_ADMIN)).to.be.bignumber.equal(GROUPS.ADMIN); - expect(await this.manager.getGroupGuardian(GROUPS.SOME)).to.be.bignumber.equal(GROUPS.SOME_ADMIN); - expect(await this.manager.getGroupGuardian(GROUPS.PUBLIC)).to.be.bignumber.equal(GROUPS.ADMIN); - // group members - expect(await this.manager.hasGroup(GROUPS.ADMIN, admin).then(formatAccess)).to.be.deep.equal([true, '0']); - expect(await this.manager.hasGroup(GROUPS.ADMIN, manager).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.ADMIN, member).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.ADMIN, user).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.SOME_ADMIN, admin).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.SOME_ADMIN, manager).then(formatAccess)).to.be.deep.equal([true, '0']); - expect(await this.manager.hasGroup(GROUPS.SOME_ADMIN, member).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.SOME_ADMIN, user).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.SOME, admin).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.SOME, manager).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - expect(await this.manager.hasGroup(GROUPS.PUBLIC, admin).then(formatAccess)).to.be.deep.equal([true, '0']); - expect(await this.manager.hasGroup(GROUPS.PUBLIC, manager).then(formatAccess)).to.be.deep.equal([true, '0']); - expect(await this.manager.hasGroup(GROUPS.PUBLIC, member).then(formatAccess)).to.be.deep.equal([true, '0']); - expect(await this.manager.hasGroup(GROUPS.PUBLIC, user).then(formatAccess)).to.be.deep.equal([true, '0']); + it('roles are correctly initialized', async function () { + // role admin + expect(await this.manager.getRoleAdmin(ROLES.ADMIN)).to.be.bignumber.equal(ROLES.ADMIN); + expect(await this.manager.getRoleAdmin(ROLES.SOME_ADMIN)).to.be.bignumber.equal(ROLES.ADMIN); + expect(await this.manager.getRoleAdmin(ROLES.SOME)).to.be.bignumber.equal(ROLES.SOME_ADMIN); + expect(await this.manager.getRoleAdmin(ROLES.PUBLIC)).to.be.bignumber.equal(ROLES.ADMIN); + // role guardian + expect(await this.manager.getRoleGuardian(ROLES.ADMIN)).to.be.bignumber.equal(ROLES.ADMIN); + expect(await this.manager.getRoleGuardian(ROLES.SOME_ADMIN)).to.be.bignumber.equal(ROLES.ADMIN); + expect(await this.manager.getRoleGuardian(ROLES.SOME)).to.be.bignumber.equal(ROLES.SOME_ADMIN); + expect(await this.manager.getRoleGuardian(ROLES.PUBLIC)).to.be.bignumber.equal(ROLES.ADMIN); + // role members + expect(await this.manager.hasRole(ROLES.ADMIN, admin).then(formatAccess)).to.be.deep.equal([true, '0']); + expect(await this.manager.hasRole(ROLES.ADMIN, manager).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.ADMIN, member).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.ADMIN, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME_ADMIN, admin).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME_ADMIN, manager).then(formatAccess)).to.be.deep.equal([true, '0']); + expect(await this.manager.hasRole(ROLES.SOME_ADMIN, member).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME_ADMIN, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, admin).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, manager).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.PUBLIC, admin).then(formatAccess)).to.be.deep.equal([true, '0']); + expect(await this.manager.hasRole(ROLES.PUBLIC, manager).then(formatAccess)).to.be.deep.equal([true, '0']); + expect(await this.manager.hasRole(ROLES.PUBLIC, member).then(formatAccess)).to.be.deep.equal([true, '0']); + expect(await this.manager.hasRole(ROLES.PUBLIC, user).then(formatAccess)).to.be.deep.equal([true, '0']); }); - describe('Groups management', function () { - describe('label group', function () { + describe('Roles management', function () { + describe('label role', function () { it('admin can emit a label event', async function () { - expectEvent(await this.manager.labelGroup(GROUPS.SOME, 'Some label', { from: admin }), 'GroupLabel', { - groupId: GROUPS.SOME, + expectEvent(await this.manager.labelRole(ROLES.SOME, 'Some label', { from: admin }), 'RoleLabel', { + roleId: ROLES.SOME, label: 'Some label', }); }); it('admin can re-emit a label event', async function () { - await this.manager.labelGroup(GROUPS.SOME, 'Some label', { from: admin }); + await this.manager.labelRole(ROLES.SOME, 'Some label', { from: admin }); - expectEvent(await this.manager.labelGroup(GROUPS.SOME, 'Updated label', { from: admin }), 'GroupLabel', { - groupId: GROUPS.SOME, + expectEvent(await this.manager.labelRole(ROLES.SOME, 'Updated label', { from: admin }), 'RoleLabel', { + roleId: ROLES.SOME, label: 'Updated label', }); }); it('emitting a label is restricted', async function () { await expectRevertCustomError( - this.manager.labelGroup(GROUPS.SOME, 'Invalid label', { from: other }), + this.manager.labelRole(ROLES.SOME, 'Invalid label', { from: other }), 'AccessManagerUnauthorizedAccount', - [other, GROUPS.ADMIN], + [other, ROLES.ADMIN], ); }); }); - describe('grant group', function () { + describe('grant role', function () { describe('without a grant delay', function () { it('without an execute delay', async function () { - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - const { receipt } = await this.manager.grantGroup(GROUPS.SOME, user, 0, { from: manager }); + const { receipt } = await this.manager.grantRole(ROLES.SOME, user, 0, { from: manager }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); - expectEvent(receipt, 'GroupGranted', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGranted', { + roleId: ROLES.SOME, account: user, since: timestamp, delay: '0', newMember: true, }); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([true, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([true, '0']); - const access = await this.manager.getAccess(GROUPS.SOME, user); - expect(access[0]).to.be.bignumber.equal(timestamp); // inGroupSince + const access = await this.manager.getAccess(ROLES.SOME, user); + expect(access[0]).to.be.bignumber.equal(timestamp); // inRoleSince expect(access[1]).to.be.bignumber.equal('0'); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect }); it('with an execute delay', async function () { - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - const { receipt } = await this.manager.grantGroup(GROUPS.SOME, user, executeDelay, { from: manager }); + const { receipt } = await this.manager.grantRole(ROLES.SOME, user, executeDelay, { from: manager }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); - expectEvent(receipt, 'GroupGranted', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGranted', { + roleId: ROLES.SOME, account: user, since: timestamp, delay: executeDelay, newMember: true, }); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([ + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([ true, executeDelay.toString(), ]); - const access = await this.manager.getAccess(GROUPS.SOME, user); - expect(access[0]).to.be.bignumber.equal(timestamp); // inGroupSince + const access = await this.manager.getAccess(ROLES.SOME, user); + expect(access[0]).to.be.bignumber.equal(timestamp); // inRoleSince expect(access[1]).to.be.bignumber.equal(executeDelay); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect }); - it('to a user that is already in the group', async function () { - expect(await this.manager.hasGroup(GROUPS.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); - await this.manager.grantGroup(GROUPS.SOME, member, 0, { from: manager }); - expect(await this.manager.hasGroup(GROUPS.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); + it('to a user that is already in the role', async function () { + expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); + await this.manager.grantRole(ROLES.SOME, member, 0, { from: manager }); + expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); }); - it('to a user that is scheduled for joining the group', async function () { - await this.manager.$_grantGroup(GROUPS.SOME, user, 10, 0); // grant delay 10 - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - await this.manager.grantGroup(GROUPS.SOME, user, 0, { from: manager }); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + it('to a user that is scheduled for joining the role', async function () { + await this.manager.$_grantRole(ROLES.SOME, user, 10, 0); // grant delay 10 + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + await this.manager.grantRole(ROLES.SOME, user, 0, { from: manager }); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); }); - it('grant group is restricted', async function () { + it('grant role is restricted', async function () { await expectRevertCustomError( - this.manager.grantGroup(GROUPS.SOME, user, 0, { from: other }), + this.manager.grantRole(ROLES.SOME, user, 0, { from: other }), 'AccessManagerUnauthorizedAccount', - [other, GROUPS.SOME_ADMIN], + [other, ROLES.SOME_ADMIN], ); }); }); describe('with a grant delay', function () { beforeEach(async function () { - await this.manager.$_setGrantDelay(GROUPS.SOME, grantDelay); + await this.manager.$_setGrantDelay(ROLES.SOME, grantDelay); await time.increase(MINSETBACK); }); - it('granted group is not active immediately', async function () { - const { receipt } = await this.manager.grantGroup(GROUPS.SOME, user, 0, { from: manager }); + it('granted role is not active immediately', async function () { + const { receipt } = await this.manager.grantRole(ROLES.SOME, user, 0, { from: manager }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); - expectEvent(receipt, 'GroupGranted', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGranted', { + roleId: ROLES.SOME, account: user, since: timestamp.add(grantDelay), delay: '0', newMember: true, }); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - const access = await this.manager.getAccess(GROUPS.SOME, user); - expect(access[0]).to.be.bignumber.equal(timestamp.add(grantDelay)); // inGroupSince + const access = await this.manager.getAccess(ROLES.SOME, user); + expect(access[0]).to.be.bignumber.equal(timestamp.add(grantDelay)); // inRoleSince expect(access[1]).to.be.bignumber.equal('0'); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect }); - it('granted group is active after the delay', async function () { - const { receipt } = await this.manager.grantGroup(GROUPS.SOME, user, 0, { from: manager }); + it('granted role is active after the delay', async function () { + const { receipt } = await this.manager.grantRole(ROLES.SOME, user, 0, { from: manager }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); - expectEvent(receipt, 'GroupGranted', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGranted', { + roleId: ROLES.SOME, account: user, since: timestamp.add(grantDelay), delay: '0', @@ -216,153 +215,153 @@ contract('AccessManager', function (accounts) { await time.increase(grantDelay); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([true, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([true, '0']); - const access = await this.manager.getAccess(GROUPS.SOME, user); - expect(access[0]).to.be.bignumber.equal(timestamp.add(grantDelay)); // inGroupSince + const access = await this.manager.getAccess(ROLES.SOME, user); + expect(access[0]).to.be.bignumber.equal(timestamp.add(grantDelay)); // inRoleSince expect(access[1]).to.be.bignumber.equal('0'); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect }); }); - it('cannot grant public group', async function () { + it('cannot grant public role', async function () { await expectRevertCustomError( - this.manager.$_grantGroup(GROUPS.PUBLIC, other, 0, executeDelay, { from: manager }), - 'AccessManagerLockedGroup', - [GROUPS.PUBLIC], + this.manager.$_grantRole(ROLES.PUBLIC, other, 0, executeDelay, { from: manager }), + 'AccessManagerLockedRole', + [ROLES.PUBLIC], ); }); }); - describe('revoke group', function () { - it('from a user that is already in the group', async function () { - expect(await this.manager.hasGroup(GROUPS.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); + describe('revoke role', function () { + it('from a user that is already in the role', async function () { + expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); - const { receipt } = await this.manager.revokeGroup(GROUPS.SOME, member, { from: manager }); - expectEvent(receipt, 'GroupRevoked', { groupId: GROUPS.SOME, account: member }); + const { receipt } = await this.manager.revokeRole(ROLES.SOME, member, { from: manager }); + expectEvent(receipt, 'RoleRevoked', { roleId: ROLES.SOME, account: member }); - expect(await this.manager.hasGroup(GROUPS.SOME, member).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([false, '0']); - const access = await this.manager.getAccess(GROUPS.SOME, user); - expect(access[0]).to.be.bignumber.equal('0'); // inGroupSince + const access = await this.manager.getAccess(ROLES.SOME, user); + expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince expect(access[1]).to.be.bignumber.equal('0'); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect }); - it('from a user that is scheduled for joining the group', async function () { - await this.manager.$_grantGroup(GROUPS.SOME, user, 10, 0); // grant delay 10 + it('from a user that is scheduled for joining the role', async function () { + await this.manager.$_grantRole(ROLES.SOME, user, 10, 0); // grant delay 10 - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - const { receipt } = await this.manager.revokeGroup(GROUPS.SOME, user, { from: manager }); - expectEvent(receipt, 'GroupRevoked', { groupId: GROUPS.SOME, account: user }); + const { receipt } = await this.manager.revokeRole(ROLES.SOME, user, { from: manager }); + expectEvent(receipt, 'RoleRevoked', { roleId: ROLES.SOME, account: user }); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - const access = await this.manager.getAccess(GROUPS.SOME, user); - expect(access[0]).to.be.bignumber.equal('0'); // inGroupSince + const access = await this.manager.getAccess(ROLES.SOME, user); + expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince expect(access[1]).to.be.bignumber.equal('0'); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect }); - it('from a user that is not in the group', async function () { - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - await this.manager.revokeGroup(GROUPS.SOME, user, { from: manager }); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + it('from a user that is not in the role', async function () { + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + await this.manager.revokeRole(ROLES.SOME, user, { from: manager }); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); }); - it('revoke group is restricted', async function () { + it('revoke role is restricted', async function () { await expectRevertCustomError( - this.manager.revokeGroup(GROUPS.SOME, member, { from: other }), + this.manager.revokeRole(ROLES.SOME, member, { from: other }), 'AccessManagerUnauthorizedAccount', - [other, GROUPS.SOME_ADMIN], + [other, ROLES.SOME_ADMIN], ); }); }); - describe('renounce group', function () { - it('for a user that is already in the group', async function () { - expect(await this.manager.hasGroup(GROUPS.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); + describe('renounce role', function () { + it('for a user that is already in the role', async function () { + expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']); - const { receipt } = await this.manager.renounceGroup(GROUPS.SOME, member, { from: member }); - expectEvent(receipt, 'GroupRevoked', { groupId: GROUPS.SOME, account: member }); + const { receipt } = await this.manager.renounceRole(ROLES.SOME, member, { from: member }); + expectEvent(receipt, 'RoleRevoked', { roleId: ROLES.SOME, account: member }); - expect(await this.manager.hasGroup(GROUPS.SOME, member).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([false, '0']); - const access = await this.manager.getAccess(GROUPS.SOME, member); - expect(access[0]).to.be.bignumber.equal('0'); // inGroupSince + const access = await this.manager.getAccess(ROLES.SOME, member); + expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince expect(access[1]).to.be.bignumber.equal('0'); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect }); - it('for a user that is schedule for joining the group', async function () { - await this.manager.$_grantGroup(GROUPS.SOME, user, 10, 0); // grant delay 10 + it('for a user that is schedule for joining the role', async function () { + await this.manager.$_grantRole(ROLES.SOME, user, 10, 0); // grant delay 10 - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - const { receipt } = await this.manager.renounceGroup(GROUPS.SOME, user, { from: user }); - expectEvent(receipt, 'GroupRevoked', { groupId: GROUPS.SOME, account: user }); + const { receipt } = await this.manager.renounceRole(ROLES.SOME, user, { from: user }); + expectEvent(receipt, 'RoleRevoked', { roleId: ROLES.SOME, account: user }); - expect(await this.manager.hasGroup(GROUPS.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); + expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']); - const access = await this.manager.getAccess(GROUPS.SOME, user); - expect(access[0]).to.be.bignumber.equal('0'); // inGroupSince + const access = await this.manager.getAccess(ROLES.SOME, user); + expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince expect(access[1]).to.be.bignumber.equal('0'); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect }); - it('for a user that is not in the group', async function () { - await this.manager.renounceGroup(GROUPS.SOME, user, { from: user }); + it('for a user that is not in the role', async function () { + await this.manager.renounceRole(ROLES.SOME, user, { from: user }); }); it('bad user confirmation', async function () { await expectRevertCustomError( - this.manager.renounceGroup(GROUPS.SOME, member, { from: user }), + this.manager.renounceRole(ROLES.SOME, member, { from: user }), 'AccessManagerBadConfirmation', [], ); }); }); - describe('change group admin', function () { - it("admin can set any group's admin", async function () { - expect(await this.manager.getGroupAdmin(GROUPS.SOME)).to.be.bignumber.equal(GROUPS.SOME_ADMIN); + describe('change role admin', function () { + it("admin can set any role's admin", async function () { + expect(await this.manager.getRoleAdmin(ROLES.SOME)).to.be.bignumber.equal(ROLES.SOME_ADMIN); - const { receipt } = await this.manager.setGroupAdmin(GROUPS.SOME, GROUPS.ADMIN, { from: admin }); - expectEvent(receipt, 'GroupAdminChanged', { groupId: GROUPS.SOME, admin: GROUPS.ADMIN }); + const { receipt } = await this.manager.setRoleAdmin(ROLES.SOME, ROLES.ADMIN, { from: admin }); + expectEvent(receipt, 'RoleAdminChanged', { roleId: ROLES.SOME, admin: ROLES.ADMIN }); - expect(await this.manager.getGroupAdmin(GROUPS.SOME)).to.be.bignumber.equal(GROUPS.ADMIN); + expect(await this.manager.getRoleAdmin(ROLES.SOME)).to.be.bignumber.equal(ROLES.ADMIN); }); - it("setting a group's admin is restricted", async function () { + it("setting a role's admin is restricted", async function () { await expectRevertCustomError( - this.manager.setGroupAdmin(GROUPS.SOME, GROUPS.SOME, { from: manager }), + this.manager.setRoleAdmin(ROLES.SOME, ROLES.SOME, { from: manager }), 'AccessManagerUnauthorizedAccount', - [manager, GROUPS.ADMIN], + [manager, ROLES.ADMIN], ); }); }); - describe('change group guardian', function () { - it("admin can set any group's admin", async function () { - expect(await this.manager.getGroupGuardian(GROUPS.SOME)).to.be.bignumber.equal(GROUPS.SOME_ADMIN); + describe('change role guardian', function () { + it("admin can set any role's admin", async function () { + expect(await this.manager.getRoleGuardian(ROLES.SOME)).to.be.bignumber.equal(ROLES.SOME_ADMIN); - const { receipt } = await this.manager.setGroupGuardian(GROUPS.SOME, GROUPS.ADMIN, { from: admin }); - expectEvent(receipt, 'GroupGuardianChanged', { groupId: GROUPS.SOME, guardian: GROUPS.ADMIN }); + const { receipt } = await this.manager.setRoleGuardian(ROLES.SOME, ROLES.ADMIN, { from: admin }); + expectEvent(receipt, 'RoleGuardianChanged', { roleId: ROLES.SOME, guardian: ROLES.ADMIN }); - expect(await this.manager.getGroupGuardian(GROUPS.SOME)).to.be.bignumber.equal(GROUPS.ADMIN); + expect(await this.manager.getRoleGuardian(ROLES.SOME)).to.be.bignumber.equal(ROLES.ADMIN); }); - it("setting a group's admin is restricted", async function () { + it("setting a role's admin is restricted", async function () { await expectRevertCustomError( - this.manager.setGroupGuardian(GROUPS.SOME, GROUPS.SOME, { from: other }), + this.manager.setRoleGuardian(ROLES.SOME, ROLES.SOME, { from: other }), 'AccessManagerUnauthorizedAccount', - [other, GROUPS.ADMIN], + [other, ROLES.ADMIN], ); }); }); @@ -372,21 +371,21 @@ contract('AccessManager', function (accounts) { const oldDelay = web3.utils.toBN(10); const newDelay = web3.utils.toBN(100); - // group is already granted (with no delay) in the initial setup. this update takes time. - await this.manager.$_grantGroup(GROUPS.SOME, member, 0, oldDelay); + // role is already granted (with no delay) in the initial setup. this update takes time. + await this.manager.$_grantRole(ROLES.SOME, member, 0, oldDelay); - const accessBefore = await this.manager.getAccess(GROUPS.SOME, member); + const accessBefore = await this.manager.getAccess(ROLES.SOME, member); expect(accessBefore[1]).to.be.bignumber.equal(oldDelay); // currentDelay expect(accessBefore[2]).to.be.bignumber.equal('0'); // pendingDelay expect(accessBefore[3]).to.be.bignumber.equal('0'); // effect - const { receipt } = await this.manager.grantGroup(GROUPS.SOME, member, newDelay, { + const { receipt } = await this.manager.grantRole(ROLES.SOME, member, newDelay, { from: manager, }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); - expectEvent(receipt, 'GroupGranted', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGranted', { + roleId: ROLES.SOME, account: member, since: timestamp, delay: newDelay, @@ -394,7 +393,7 @@ contract('AccessManager', function (accounts) { }); // immediate effect - const accessAfter = await this.manager.getAccess(GROUPS.SOME, member); + const accessAfter = await this.manager.getAccess(ROLES.SOME, member); expect(accessAfter[1]).to.be.bignumber.equal(newDelay); // currentDelay expect(accessAfter[2]).to.be.bignumber.equal('0'); // pendingDelay expect(accessAfter[3]).to.be.bignumber.equal('0'); // effect @@ -404,22 +403,22 @@ contract('AccessManager', function (accounts) { const oldDelay = web3.utils.toBN(100); const newDelay = web3.utils.toBN(10); - // group is already granted (with no delay) in the initial setup. this update takes time. - await this.manager.$_grantGroup(GROUPS.SOME, member, 0, oldDelay); + // role is already granted (with no delay) in the initial setup. this update takes time. + await this.manager.$_grantRole(ROLES.SOME, member, 0, oldDelay); - const accessBefore = await this.manager.getAccess(GROUPS.SOME, member); + const accessBefore = await this.manager.getAccess(ROLES.SOME, member); expect(accessBefore[1]).to.be.bignumber.equal(oldDelay); // currentDelay expect(accessBefore[2]).to.be.bignumber.equal('0'); // pendingDelay expect(accessBefore[3]).to.be.bignumber.equal('0'); // effect - const { receipt } = await this.manager.grantGroup(GROUPS.SOME, member, newDelay, { + const { receipt } = await this.manager.grantRole(ROLES.SOME, member, newDelay, { from: manager, }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); const setback = oldDelay.sub(newDelay); - expectEvent(receipt, 'GroupGranted', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGranted', { + roleId: ROLES.SOME, account: member, since: timestamp.add(setback), delay: newDelay, @@ -427,29 +426,29 @@ contract('AccessManager', function (accounts) { }); // no immediate effect - const accessAfter = await this.manager.getAccess(GROUPS.SOME, member); + const accessAfter = await this.manager.getAccess(ROLES.SOME, member); expect(accessAfter[1]).to.be.bignumber.equal(oldDelay); // currentDelay expect(accessAfter[2]).to.be.bignumber.equal(newDelay); // pendingDelay expect(accessAfter[3]).to.be.bignumber.equal(timestamp.add(setback)); // effect // delayed effect await time.increase(setback); - const accessAfterSetback = await this.manager.getAccess(GROUPS.SOME, member); + const accessAfterSetback = await this.manager.getAccess(ROLES.SOME, member); expect(accessAfterSetback[1]).to.be.bignumber.equal(newDelay); // currentDelay expect(accessAfterSetback[2]).to.be.bignumber.equal('0'); // pendingDelay expect(accessAfterSetback[3]).to.be.bignumber.equal('0'); // effect }); it('can set a user execution delay during the grant delay', async function () { - await this.manager.$_grantGroup(GROUPS.SOME, other, 10, 0); - // here: "other" is pending to get the group, but doesn't yet have it. + await this.manager.$_grantRole(ROLES.SOME, other, 10, 0); + // here: "other" is pending to get the role, but doesn't yet have it. - const { receipt } = await this.manager.grantGroup(GROUPS.SOME, other, executeDelay, { from: manager }); + const { receipt } = await this.manager.grantRole(ROLES.SOME, other, executeDelay, { from: manager }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); // increasing the execution delay from 0 to executeDelay is immediate - expectEvent(receipt, 'GroupGranted', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGranted', { + roleId: ROLES.SOME, account: other, since: timestamp, delay: executeDelay, @@ -463,82 +462,82 @@ contract('AccessManager', function (accounts) { const oldDelay = web3.utils.toBN(10); const newDelay = web3.utils.toBN(100); - await this.manager.$_setGrantDelay(GROUPS.SOME, oldDelay); + await this.manager.$_setGrantDelay(ROLES.SOME, oldDelay); await time.increase(MINSETBACK); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay); - const { receipt } = await this.manager.setGrantDelay(GROUPS.SOME, newDelay, { from: admin }); + const { receipt } = await this.manager.setGrantDelay(ROLES.SOME, newDelay, { from: admin }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay)); expect(setback).to.be.bignumber.equal(MINSETBACK); - expectEvent(receipt, 'GroupGrantDelayChanged', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGrantDelayChanged', { + roleId: ROLES.SOME, delay: newDelay, since: timestamp.add(setback), }); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay); await time.increase(setback); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(newDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(newDelay); }); it('increasing the delay has delay effect #1', async function () { const oldDelay = web3.utils.toBN(100); const newDelay = web3.utils.toBN(10); - await this.manager.$_setGrantDelay(GROUPS.SOME, oldDelay); + await this.manager.$_setGrantDelay(ROLES.SOME, oldDelay); await time.increase(MINSETBACK); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay); - const { receipt } = await this.manager.setGrantDelay(GROUPS.SOME, newDelay, { from: admin }); + const { receipt } = await this.manager.setGrantDelay(ROLES.SOME, newDelay, { from: admin }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay)); expect(setback).to.be.bignumber.equal(MINSETBACK); - expectEvent(receipt, 'GroupGrantDelayChanged', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGrantDelayChanged', { + roleId: ROLES.SOME, delay: newDelay, since: timestamp.add(setback), }); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay); await time.increase(setback); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(newDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(newDelay); }); it('increasing the delay has delay effect #2', async function () { const oldDelay = time.duration.days(30); // more than the minsetback const newDelay = web3.utils.toBN(10); - await this.manager.$_setGrantDelay(GROUPS.SOME, oldDelay); + await this.manager.$_setGrantDelay(ROLES.SOME, oldDelay); await time.increase(MINSETBACK); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay); - const { receipt } = await this.manager.setGrantDelay(GROUPS.SOME, newDelay, { from: admin }); + const { receipt } = await this.manager.setGrantDelay(ROLES.SOME, newDelay, { from: admin }); const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN); const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay)); expect(setback).to.be.bignumber.gt(MINSETBACK); - expectEvent(receipt, 'GroupGrantDelayChanged', { - groupId: GROUPS.SOME, + expectEvent(receipt, 'RoleGrantDelayChanged', { + roleId: ROLES.SOME, delay: newDelay, since: timestamp.add(setback), }); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay); await time.increase(setback); - expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(newDelay); + expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(newDelay); }); it('changing the grant delay is restricted', async function () { await expectRevertCustomError( - this.manager.setGrantDelay(GROUPS.SOME, grantDelay, { from: other }), + this.manager.setGrantDelay(ROLES.SOME, grantDelay, { from: other }), 'AccessManagerUnauthorizedAccount', - [GROUPS.ADMIN, other], + [ROLES.ADMIN, other], ); }); }); @@ -562,84 +561,75 @@ contract('AccessManager', function (accounts) { describe('Change function permissions', function () { const sigs = ['someFunction()', 'someOtherFunction(uint256)', 'oneMoreFunction(address,uint8)'].map(selector); - it('admin can set function group', async function () { + it('admin can set function role', async function () { for (const sig of sigs) { - expect(await this.manager.getTargetFunctionGroup(this.target.address, sig)).to.be.bignumber.equal( - GROUPS.ADMIN, - ); + expect(await this.manager.getTargetFunctionRole(this.target.address, sig)).to.be.bignumber.equal(ROLES.ADMIN); } - const { receipt: receipt1 } = await this.manager.setTargetFunctionGroup( - this.target.address, - sigs, - GROUPS.SOME, - { - from: admin, - }, - ); - - for (const sig of sigs) { - expectEvent(receipt1, 'TargetFunctionGroupUpdated', { - target: this.target.address, - selector: sig, - groupId: GROUPS.SOME, - }); - expect(await this.manager.getTargetFunctionGroup(this.target.address, sig)).to.be.bignumber.equal( - GROUPS.SOME, - ); - } - - const { receipt: receipt2 } = await this.manager.setTargetFunctionGroup( - this.target.address, - [sigs[1]], - GROUPS.SOME_ADMIN, - { - from: admin, - }, - ); - expectEvent(receipt2, 'TargetFunctionGroupUpdated', { - target: this.target.address, - selector: sigs[1], - groupId: GROUPS.SOME_ADMIN, + const { receipt: receipt1 } = await this.manager.setTargetFunctionRole(this.target.address, sigs, ROLES.SOME, { + from: admin, }); for (const sig of sigs) { - expect(await this.manager.getTargetFunctionGroup(this.target.address, sig)).to.be.bignumber.equal( - sig == sigs[1] ? GROUPS.SOME_ADMIN : GROUPS.SOME, + expectEvent(receipt1, 'TargetFunctionRoleUpdated', { + target: this.target.address, + selector: sig, + roleId: ROLES.SOME, + }); + expect(await this.manager.getTargetFunctionRole(this.target.address, sig)).to.be.bignumber.equal(ROLES.SOME); + } + + const { receipt: receipt2 } = await this.manager.setTargetFunctionRole( + this.target.address, + [sigs[1]], + ROLES.SOME_ADMIN, + { + from: admin, + }, + ); + expectEvent(receipt2, 'TargetFunctionRoleUpdated', { + target: this.target.address, + selector: sigs[1], + roleId: ROLES.SOME_ADMIN, + }); + + for (const sig of sigs) { + expect(await this.manager.getTargetFunctionRole(this.target.address, sig)).to.be.bignumber.equal( + sig == sigs[1] ? ROLES.SOME_ADMIN : ROLES.SOME, ); } }); - it('non-admin cannot set function group', async function () { + it('non-admin cannot set function role', async function () { await expectRevertCustomError( - this.manager.setTargetFunctionGroup(this.target.address, sigs, GROUPS.SOME, { from: other }), + this.manager.setTargetFunctionRole(this.target.address, sigs, ROLES.SOME, { from: other }), 'AccessManagerUnauthorizedAccount', - [other, GROUPS.ADMIN], + [other, ROLES.ADMIN], ); }); }); // WIP describe('Calling restricted & unrestricted functions', function () { - for (const [callerGroups, fnGroup, closed, delay] of product( - [[], [GROUPS.SOME]], - [undefined, GROUPS.ADMIN, GROUPS.SOME, GROUPS.PUBLIC], + for (const [callerRoles, fnRole, closed, delay] of product( + [[], [ROLES.SOME]], + [undefined, ROLES.ADMIN, ROLES.SOME, ROLES.PUBLIC], [false, true], [null, executeDelay], )) { // can we call with a delay ? - const indirectSuccess = (fnGroup == GROUPS.PUBLIC || callerGroups.includes(fnGroup)) && !closed; + const indirectSuccess = (fnRole == ROLES.PUBLIC || callerRoles.includes(fnRole)) && !closed; // can we call without a delay ? - const directSuccess = (fnGroup == GROUPS.PUBLIC || (callerGroups.includes(fnGroup) && !delay)) && !closed; + const directSuccess = (fnRole == ROLES.PUBLIC || (callerRoles.includes(fnRole) && !delay)) && !closed; const description = [ - 'Caller in groups', - '[' + (callerGroups ?? []).map(groupId => GROUPS[groupId]).join(', ') + ']', + 'Caller in roles', + '[' + (callerRoles ?? []).map(roleId => ROLES[roleId]).join(', ') + ']', delay ? 'with a delay' : 'without a delay', '+', - 'functions open to groups', - '[' + (GROUPS[fnGroup] ?? '') + ']', + 'functions open to roles', + '[' + (ROLES[fnRole] ?? '') + ']', closed ? `(closed)` : '', ].join(' '); @@ -648,36 +638,34 @@ contract('AccessManager', function (accounts) { // setup await Promise.all([ this.manager.$_setTargetClosed(this.target.address, closed), - fnGroup && - this.manager.$_setTargetFunctionGroup(this.target.address, selector('fnRestricted()'), fnGroup), - fnGroup && - this.manager.$_setTargetFunctionGroup(this.target.address, selector('fnUnrestricted()'), fnGroup), - ...callerGroups - .filter(groupId => groupId != GROUPS.PUBLIC) - .map(groupId => this.manager.$_grantGroup(groupId, user, 0, delay ?? 0)), + fnRole && this.manager.$_setTargetFunctionRole(this.target.address, selector('fnRestricted()'), fnRole), + fnRole && this.manager.$_setTargetFunctionRole(this.target.address, selector('fnUnrestricted()'), fnRole), + ...callerRoles + .filter(roleId => roleId != ROLES.PUBLIC) + .map(roleId => this.manager.$_grantRole(roleId, user, 0, delay ?? 0)), ]); // post setup checks expect(await this.manager.isTargetClosed(this.target.address)).to.be.equal(closed); - if (fnGroup) { + if (fnRole) { expect( - await this.manager.getTargetFunctionGroup(this.target.address, selector('fnRestricted()')), - ).to.be.bignumber.equal(fnGroup); + await this.manager.getTargetFunctionRole(this.target.address, selector('fnRestricted()')), + ).to.be.bignumber.equal(fnRole); expect( - await this.manager.getTargetFunctionGroup(this.target.address, selector('fnUnrestricted()')), - ).to.be.bignumber.equal(fnGroup); + await this.manager.getTargetFunctionRole(this.target.address, selector('fnUnrestricted()')), + ).to.be.bignumber.equal(fnRole); } - for (const groupId of callerGroups) { - const access = await this.manager.getAccess(groupId, user); - if (groupId == GROUPS.PUBLIC) { - expect(access[0]).to.be.bignumber.equal('0'); // inGroupSince + for (const roleId of callerRoles) { + const access = await this.manager.getAccess(roleId, user); + if (roleId == ROLES.PUBLIC) { + expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince expect(access[1]).to.be.bignumber.equal('0'); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect } else { - expect(access[0]).to.be.bignumber.gt('0'); // inGroupSince + expect(access[0]).to.be.bignumber.gt('0'); // inRoleSince expect(access[1]).to.be.bignumber.eq(String(delay ?? 0)); // currentDelay expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay expect(access[3]).to.be.bignumber.equal('0'); // effect @@ -745,7 +733,7 @@ contract('AccessManager', function (accounts) { if (directSuccess) { const { receipt, tx } = await this.execute(); await expectEvent.inTransaction(tx, this.target, 'CalledRestricted', { caller: this.manager.address }); - if (delay && fnGroup !== GROUPS.PUBLIC) { + if (delay && fnRole !== ROLES.PUBLIC) { expectEvent(receipt, 'OperationExecuted', { operationId: this.opId }); expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal('0'); } @@ -783,7 +771,7 @@ contract('AccessManager', function (accounts) { if (directSuccess || indirectSuccess) { const { receipt, tx } = await this.execute(); await expectEvent.inTransaction(tx, this.target, 'CalledRestricted', { caller: this.manager.address }); - if (delay && fnGroup !== GROUPS.PUBLIC) { + if (delay && fnRole !== ROLES.PUBLIC) { expectEvent(receipt, 'OperationExecuted', { operationId: this.opId }); expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal('0'); } @@ -879,8 +867,8 @@ contract('AccessManager', function (accounts) { describe('Indirect execution corner-cases', async function () { beforeEach(async function () { - await this.manager.$_setTargetFunctionGroup(this.target.address, this.callData, GROUPS.SOME); - await this.manager.$_grantGroup(GROUPS.SOME, user, 0, executeDelay); + await this.manager.$_setTargetFunctionRole(this.target.address, this.callData, ROLES.SOME); + await this.manager.$_grantRole(ROLES.SOME, user, 0, executeDelay); }); it('Checking canCall when caller is the manager depend on the _executionId', async function () { @@ -1002,13 +990,13 @@ contract('AccessManager', function (accounts) { }); describe('with Ownable target contract', function () { - const groupId = web3.utils.toBN(1); + const roleId = web3.utils.toBN(1); beforeEach(async function () { this.ownable = await Ownable.new(this.manager.address); - // add user to group - await this.manager.$_grantGroup(groupId, user, 0, 0); + // add user to role + await this.manager.$_grantRole(roleId, user, 0, 0); }); it('initial state', async function () { @@ -1024,7 +1012,7 @@ contract('AccessManager', function (accounts) { await expectRevertCustomError(this.ownable.$_checkOwner({ from: user }), 'OwnableUnauthorizedAccount', [user]); }); - it('relayed call (with group): reverts', async function () { + it('relayed call (with role): reverts', async function () { await expectRevertCustomError( this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: user }), 'AccessManagerUnauthorizedCall', @@ -1032,7 +1020,7 @@ contract('AccessManager', function (accounts) { ); }); - it('relayed call (without group): reverts', async function () { + it('relayed call (without role): reverts', async function () { await expectRevertCustomError( this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: other }), 'AccessManagerUnauthorizedCall', @@ -1042,9 +1030,9 @@ contract('AccessManager', function (accounts) { }); describe('Contract is managed', function () { - describe('function is open to specific group', function () { + describe('function is open to specific role', function () { beforeEach(async function () { - await this.manager.$_setTargetFunctionGroup(this.ownable.address, selector('$_checkOwner()'), groupId); + await this.manager.$_setTargetFunctionRole(this.ownable.address, selector('$_checkOwner()'), roleId); }); it('directly call: reverts', async function () { @@ -1053,11 +1041,11 @@ contract('AccessManager', function (accounts) { ]); }); - it('relayed call (with group): success', async function () { + it('relayed call (with role): success', async function () { await this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: user }); }); - it('relayed call (without group): reverts', async function () { + it('relayed call (without role): reverts', async function () { await expectRevertCustomError( this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: other }), 'AccessManagerUnauthorizedCall', @@ -1066,9 +1054,9 @@ contract('AccessManager', function (accounts) { }); }); - describe('function is open to public group', function () { + describe('function is open to public role', function () { beforeEach(async function () { - await this.manager.$_setTargetFunctionGroup(this.ownable.address, selector('$_checkOwner()'), GROUPS.PUBLIC); + await this.manager.$_setTargetFunctionRole(this.ownable.address, selector('$_checkOwner()'), ROLES.PUBLIC); }); it('directly call: reverts', async function () { @@ -1077,11 +1065,11 @@ contract('AccessManager', function (accounts) { ]); }); - it('relayed call (with group): success', async function () { + it('relayed call (with role): success', async function () { await this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: user }); }); - it('relayed call (without group): success', async function () { + it('relayed call (without role): success', async function () { await this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: other }); }); }); @@ -1115,7 +1103,7 @@ contract('AccessManager', function (accounts) { await expectRevertCustomError( this.manager.updateAuthority(this.target.address, this.newManager.address, { from: other }), 'AccessManagerUnauthorizedAccount', - [other, GROUPS.ADMIN], + [other, ROLES.ADMIN], ); }); diff --git a/test/governance/extensions/GovernorTimelockAccess.test.js b/test/governance/extensions/GovernorTimelockAccess.test.js index ad533296a..c6e230a31 100644 --- a/test/governance/extensions/GovernorTimelockAccess.test.js +++ b/test/governance/extensions/GovernorTimelockAccess.test.js @@ -130,12 +130,12 @@ contract('GovernorTimelockAccess', function (accounts) { it('single operation with access manager delay', async function () { const delay = 1000; - const groupId = '1'; + const roleId = '1'; - await this.manager.setTargetFunctionGroup(this.receiver.address, [this.restricted.selector], groupId, { + await this.manager.setTargetFunctionRole(this.receiver.address, [this.restricted.selector], roleId, { from: admin, }); - await this.manager.grantGroup(groupId, this.mock.address, delay, { from: admin }); + await this.manager.grantRole(roleId, this.mock.address, delay, { from: admin }); this.proposal = await this.helper.setProposal([this.restricted.operation], 'descr'); @@ -167,16 +167,16 @@ contract('GovernorTimelockAccess', function (accounts) { it('bundle of varied operations', async function () { const managerDelay = 1000; - const groupId = '1'; + const roleId = '1'; const baseDelay = managerDelay * 2; await this.mock.$_setBaseDelaySeconds(baseDelay); - await this.manager.setTargetFunctionGroup(this.receiver.address, [this.restricted.selector], groupId, { + await this.manager.setTargetFunctionRole(this.receiver.address, [this.restricted.selector], roleId, { from: admin, }); - await this.manager.grantGroup(groupId, this.mock.address, managerDelay, { from: admin }); + await this.manager.grantRole(roleId, this.mock.address, managerDelay, { from: admin }); this.proposal = await this.helper.setProposal( [this.restricted.operation, this.unrestricted.operation], @@ -212,12 +212,12 @@ contract('GovernorTimelockAccess', function (accounts) { it('cancellation after queue (internal)', async function () { const delay = 1000; - const groupId = '1'; + const roleId = '1'; - await this.manager.setTargetFunctionGroup(this.receiver.address, [this.restricted.selector], groupId, { + await this.manager.setTargetFunctionRole(this.receiver.address, [this.restricted.selector], roleId, { from: admin, }); - await this.manager.grantGroup(groupId, this.mock.address, delay, { from: admin }); + await this.manager.grantRole(roleId, this.mock.address, delay, { from: admin }); this.proposal = await this.helper.setProposal([this.restricted.operation], 'descr');