Contract Name:
StrictRolesAuthority
Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {RolesAuthority} from "@solmate/src/auth/authorities/RolesAuthority.sol";
import {Authority} from "@solmate/src/auth/Auth.sol";
/**
* .-==+=======+:
* :---=-::-==:
* .-:-==-:-==:
* .:::--::::::. .--:-=--:--. .:--:::--..
* .=++=++:::::.. .:::---::--. ....::...:::.
* :::-::..::.. .::::-:::::. ...::...:::.
* ...::..::::.. .::::--::-:. ....::...:::..
* ............ ....:::..::. ------:......
* ........... ........:.... .....::..:.. ======-...... ...........
* :------:.:... ...:+***++*#+ .------:---. ...::::.:::... .....:-----::.
* .::::::::-:.. .::--..:-::.. .-=+===++=-==: ...:::..:--:.. .:==+=++++++*:
*
* @title StrictRolesAuthority
* @notice An extension of RolesAuthority that enforces stricter role management rules
* @dev This contract only allows adding roles through setUserRole() and requires admin multisig
* approval for role removal through removeUserRole()
*/
contract StrictRolesAuthority is RolesAuthority {
/// @notice The error emitted when a user attempts to remove a role
error RoleRemovalNotAllowed();
/// @notice Emitted when a role is configured as removable
event RemovableRoleConfigured(uint8 indexed role, bool allowed);
/// @notice Which roles are allowed to be removed
mapping(uint8 => bool) public isRoleRemovable;
/**
* @notice Creates a new StrictRolesAuthority instance
* @param _owner The address that will be the owner of this authority
* @param _authority The authority contract to use for authentication
*/
constructor(address _owner, Authority _authority) RolesAuthority(_owner, _authority) {}
/**
* @notice Sets a role for a user, but only allows adding roles
* @dev This function can only be called by authorized addresses
* @param user The address of the user to set the role for
* @param role The role to set
* @param enabled Must be true, as this function only allows adding roles
* @custom:reverts If enabled is false or caller is not authorized
*/
function setUserRole(address user, uint8 role, bool enabled) public virtual override requiresAuth {
if (!enabled) {
revert RoleRemovalNotAllowed();
}
getUserRoles[user] |= bytes32(1 << role);
emit UserRoleUpdated(user, role, true);
}
/**
* @notice Removes a role from a user
* @dev This function can only be called by called by the ADMIN_MULTISIG_ROLE
* @param user The address of the user to remove the role from
* @param role The role to remove
* @custom:reverts If caller is not the admin multisig
*/
function removeUserRole(address user, uint8 role) external virtual requiresAuth {
if (!isRoleRemovable[role]) {
revert RoleRemovalNotAllowed();
}
getUserRoles[user] &= ~bytes32(1 << role);
emit UserRoleUpdated(user, role, false);
}
/**
* @notice Returns all roles for a user
* @param user The address of the user to get the roles for
* @return A bitmask of all roles for the user
*/
function getAllRoles(address user) public view returns (uint256) {
return uint256(getUserRoles[user]);
}
/**
* @notice Sets a role as removable or not
* @dev This function can only be called by the owner (timelock)
* @param role The role to set as removable
* @param allowed Whether the role should be removable
* @custom:reverts If caller is not the owner
*/
function setRoleRemovable(uint8 role, bool allowed) external requiresAuth {
isRoleRemovable[role] = allowed;
emit RemovableRoleConfigured(role, allowed);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {Auth, Authority} from "../Auth.sol";
/// @notice Role based Authority that supports up to 256 roles.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/authorities/RolesAuthority.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-roles/blob/master/src/roles.sol)
contract RolesAuthority is Auth, Authority {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled);
event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled);
event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled);
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}
/*//////////////////////////////////////////////////////////////
ROLE/USER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => bytes32) public getUserRoles;
mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic;
mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability;
function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) {
return (uint256(getUserRoles[user]) >> role) & 1 != 0;
}
function doesRoleHaveCapability(
uint8 role,
address target,
bytes4 functionSig
) public view virtual returns (bool) {
return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0;
}
/*//////////////////////////////////////////////////////////////
AUTHORIZATION LOGIC
//////////////////////////////////////////////////////////////*/
function canCall(
address user,
address target,
bytes4 functionSig
) public view virtual override returns (bool) {
return
isCapabilityPublic[target][functionSig] ||
bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig];
}
/*//////////////////////////////////////////////////////////////
ROLE CAPABILITY CONFIGURATION LOGIC
//////////////////////////////////////////////////////////////*/
function setPublicCapability(
address target,
bytes4 functionSig,
bool enabled
) public virtual requiresAuth {
isCapabilityPublic[target][functionSig] = enabled;
emit PublicCapabilityUpdated(target, functionSig, enabled);
}
function setRoleCapability(
uint8 role,
address target,
bytes4 functionSig,
bool enabled
) public virtual requiresAuth {
if (enabled) {
getRolesWithCapability[target][functionSig] |= bytes32(1 << role);
} else {
getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role);
}
emit RoleCapabilityUpdated(role, target, functionSig, enabled);
}
/*//////////////////////////////////////////////////////////////
USER ROLE ASSIGNMENT LOGIC
//////////////////////////////////////////////////////////////*/
function setUserRole(
address user,
uint8 role,
bool enabled
) public virtual requiresAuth {
if (enabled) {
getUserRoles[user] |= bytes32(1 << role);
} else {
getUserRoles[user] &= ~bytes32(1 << role);
}
emit UserRoleUpdated(user, role, enabled);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
event OwnershipTransferred(address indexed user, address indexed newOwner);
event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
address public owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnershipTransferred(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifier requiresAuth() virtual {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
// aware that this makes protected functions uncallable even to the owner if the authority is out of order.
return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
function setAuthority(Authority newAuthority) public virtual {
// We check if the caller is the owner first because we want to ensure they can
// always swap out the authority even if it's reverting or using up a lot of gas.
require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
function transferOwnership(address newOwner) public virtual requiresAuth {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
function canCall(
address user,
address target,
bytes4 functionSig
) external view returns (bool);
}