mirror of
https://github.com/luxfi/uma.git
synced 2026-10-30 15:14:44 +00:00
238 lines
9.1 KiB
Solidity
238 lines
9.1 KiB
Solidity
// SPDX-License-Identifier: AGPL-3.0-only
|
|
pragma solidity ^0.8.0;
|
|
|
|
library Exclusive {
|
|
struct RoleMembership {
|
|
address member;
|
|
}
|
|
|
|
function isMember(RoleMembership storage roleMembership, address memberToCheck) internal view returns (bool) {
|
|
return roleMembership.member == memberToCheck;
|
|
}
|
|
|
|
function resetMember(RoleMembership storage roleMembership, address newMember) internal {
|
|
require(newMember != address(0x0), "Cannot set an exclusive role to 0x0");
|
|
roleMembership.member = newMember;
|
|
}
|
|
|
|
function getMember(RoleMembership storage roleMembership) internal view returns (address) {
|
|
return roleMembership.member;
|
|
}
|
|
|
|
function init(RoleMembership storage roleMembership, address initialMember) internal {
|
|
resetMember(roleMembership, initialMember);
|
|
}
|
|
}
|
|
|
|
library Shared {
|
|
struct RoleMembership {
|
|
mapping(address => bool) members;
|
|
}
|
|
|
|
function isMember(RoleMembership storage roleMembership, address memberToCheck) internal view returns (bool) {
|
|
return roleMembership.members[memberToCheck];
|
|
}
|
|
|
|
function addMember(RoleMembership storage roleMembership, address memberToAdd) internal {
|
|
require(memberToAdd != address(0x0), "Cannot add 0x0 to a shared role");
|
|
roleMembership.members[memberToAdd] = true;
|
|
}
|
|
|
|
function removeMember(RoleMembership storage roleMembership, address memberToRemove) internal {
|
|
roleMembership.members[memberToRemove] = false;
|
|
}
|
|
|
|
function init(RoleMembership storage roleMembership, address[] memory initialMembers) internal {
|
|
for (uint256 i = 0; i < initialMembers.length; i++) {
|
|
addMember(roleMembership, initialMembers[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @title Base class to manage permissions for the derived class.
|
|
*/
|
|
abstract contract MultiRole {
|
|
using Exclusive for Exclusive.RoleMembership;
|
|
using Shared for Shared.RoleMembership;
|
|
|
|
enum RoleType { Invalid, Exclusive, Shared }
|
|
|
|
struct Role {
|
|
uint256 managingRole;
|
|
RoleType roleType;
|
|
Exclusive.RoleMembership exclusiveRoleMembership;
|
|
Shared.RoleMembership sharedRoleMembership;
|
|
}
|
|
|
|
mapping(uint256 => Role) private roles;
|
|
|
|
event ResetExclusiveMember(uint256 indexed roleId, address indexed newMember, address indexed manager);
|
|
event AddedSharedMember(uint256 indexed roleId, address indexed newMember, address indexed manager);
|
|
event RemovedSharedMember(uint256 indexed roleId, address indexed oldMember, address indexed manager);
|
|
|
|
/**
|
|
* @notice Reverts unless the caller is a member of the specified roleId.
|
|
*/
|
|
modifier onlyRoleHolder(uint256 roleId) {
|
|
require(holdsRole(roleId, msg.sender), "Sender does not hold required role");
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @notice Reverts unless the caller is a member of the manager role for the specified roleId.
|
|
*/
|
|
modifier onlyRoleManager(uint256 roleId) {
|
|
require(holdsRole(roles[roleId].managingRole, msg.sender), "Can only be called by a role manager");
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @notice Reverts unless the roleId represents an initialized, exclusive roleId.
|
|
*/
|
|
modifier onlyExclusive(uint256 roleId) {
|
|
require(roles[roleId].roleType == RoleType.Exclusive, "Must be called on an initialized Exclusive role");
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @notice Reverts unless the roleId represents an initialized, shared roleId.
|
|
*/
|
|
modifier onlyShared(uint256 roleId) {
|
|
require(roles[roleId].roleType == RoleType.Shared, "Must be called on an initialized Shared role");
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @notice Whether `memberToCheck` is a member of roleId.
|
|
* @dev Reverts if roleId does not correspond to an initialized role.
|
|
* @param roleId the Role to check.
|
|
* @param memberToCheck the address to check.
|
|
* @return True if `memberToCheck` is a member of `roleId`.
|
|
*/
|
|
function holdsRole(uint256 roleId, address memberToCheck) public view returns (bool) {
|
|
Role storage role = roles[roleId];
|
|
if (role.roleType == RoleType.Exclusive) {
|
|
return role.exclusiveRoleMembership.isMember(memberToCheck);
|
|
} else if (role.roleType == RoleType.Shared) {
|
|
return role.sharedRoleMembership.isMember(memberToCheck);
|
|
}
|
|
revert("Invalid roleId");
|
|
}
|
|
|
|
/**
|
|
* @notice Changes the exclusive role holder of `roleId` to `newMember`.
|
|
* @dev Reverts if the caller is not a member of the managing role for `roleId` or if `roleId` is not an
|
|
* initialized, ExclusiveRole.
|
|
* @param roleId the ExclusiveRole membership to modify.
|
|
* @param newMember the new ExclusiveRole member.
|
|
*/
|
|
function resetMember(uint256 roleId, address newMember) public onlyExclusive(roleId) onlyRoleManager(roleId) {
|
|
roles[roleId].exclusiveRoleMembership.resetMember(newMember);
|
|
emit ResetExclusiveMember(roleId, newMember, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @notice Gets the current holder of the exclusive role, `roleId`.
|
|
* @dev Reverts if `roleId` does not represent an initialized, exclusive role.
|
|
* @param roleId the ExclusiveRole membership to check.
|
|
* @return the address of the current ExclusiveRole member.
|
|
*/
|
|
function getMember(uint256 roleId) public view onlyExclusive(roleId) returns (address) {
|
|
return roles[roleId].exclusiveRoleMembership.getMember();
|
|
}
|
|
|
|
/**
|
|
* @notice Adds `newMember` to the shared role, `roleId`.
|
|
* @dev Reverts if `roleId` does not represent an initialized, SharedRole or if the caller is not a member of the
|
|
* managing role for `roleId`.
|
|
* @param roleId the SharedRole membership to modify.
|
|
* @param newMember the new SharedRole member.
|
|
*/
|
|
function addMember(uint256 roleId, address newMember) public onlyShared(roleId) onlyRoleManager(roleId) {
|
|
roles[roleId].sharedRoleMembership.addMember(newMember);
|
|
emit AddedSharedMember(roleId, newMember, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @notice Removes `memberToRemove` from the shared role, `roleId`.
|
|
* @dev Reverts if `roleId` does not represent an initialized, SharedRole or if the caller is not a member of the
|
|
* managing role for `roleId`.
|
|
* @param roleId the SharedRole membership to modify.
|
|
* @param memberToRemove the current SharedRole member to remove.
|
|
*/
|
|
function removeMember(uint256 roleId, address memberToRemove) public onlyShared(roleId) onlyRoleManager(roleId) {
|
|
roles[roleId].sharedRoleMembership.removeMember(memberToRemove);
|
|
emit RemovedSharedMember(roleId, memberToRemove, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @notice Removes caller from the role, `roleId`.
|
|
* @dev Reverts if the caller is not a member of the role for `roleId` or if `roleId` is not an
|
|
* initialized, SharedRole.
|
|
* @param roleId the SharedRole membership to modify.
|
|
*/
|
|
function renounceMembership(uint256 roleId) public onlyShared(roleId) onlyRoleHolder(roleId) {
|
|
roles[roleId].sharedRoleMembership.removeMember(msg.sender);
|
|
emit RemovedSharedMember(roleId, msg.sender, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @notice Reverts if `roleId` is not initialized.
|
|
*/
|
|
modifier onlyValidRole(uint256 roleId) {
|
|
require(roles[roleId].roleType != RoleType.Invalid, "Attempted to use an invalid roleId");
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @notice Reverts if `roleId` is initialized.
|
|
*/
|
|
modifier onlyInvalidRole(uint256 roleId) {
|
|
require(roles[roleId].roleType == RoleType.Invalid, "Cannot use a pre-existing role");
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @notice Internal method to initialize a shared role, `roleId`, which will be managed by `managingRoleId`.
|
|
* `initialMembers` will be immediately added to the role.
|
|
* @dev Should be called by derived contracts, usually at construction time. Will revert if the role is already
|
|
* initialized.
|
|
*/
|
|
function _createSharedRole(
|
|
uint256 roleId,
|
|
uint256 managingRoleId,
|
|
address[] memory initialMembers
|
|
) internal onlyInvalidRole(roleId) {
|
|
Role storage role = roles[roleId];
|
|
role.roleType = RoleType.Shared;
|
|
role.managingRole = managingRoleId;
|
|
role.sharedRoleMembership.init(initialMembers);
|
|
require(
|
|
roles[managingRoleId].roleType != RoleType.Invalid,
|
|
"Attempted to use an invalid role to manage a shared role"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @notice Internal method to initialize an exclusive role, `roleId`, which will be managed by `managingRoleId`.
|
|
* `initialMember` will be immediately added to the role.
|
|
* @dev Should be called by derived contracts, usually at construction time. Will revert if the role is already
|
|
* initialized.
|
|
*/
|
|
function _createExclusiveRole(
|
|
uint256 roleId,
|
|
uint256 managingRoleId,
|
|
address initialMember
|
|
) internal onlyInvalidRole(roleId) {
|
|
Role storage role = roles[roleId];
|
|
role.roleType = RoleType.Exclusive;
|
|
role.managingRole = managingRoleId;
|
|
role.exclusiveRoleMembership.init(initialMember);
|
|
require(
|
|
roles[managingRoleId].roleType != RoleType.Invalid,
|
|
"Attempted to use an invalid role to manage an exclusive role"
|
|
);
|
|
}
|
|
}
|