// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "./IDelegatedAccessControl.sol"; /** * @title Modifier provider for contracts that want to interact with the ACL contract. */ // solhint-disable ordering abstract contract DelegatedAccessControl is Initializable, ContextUpgradeable, IDelegatedAccessControl, ERC165Upgradeable { // solhint-disable-next-line func-name-mixedcase, no-empty-blocks function __DelegatedAccessControl_init() internal onlyInitializing { __DelegatedAccessControl_init_unchained(); } // solhint-disable-next-line func-name-mixedcase, no-empty-blocks function __DelegatedAccessControl_init_unchained() internal onlyInitializing { _roleNames[0x00] = DELEGATED_ADMIN; } struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(address => mapping(bytes32 => RoleData)) private _roles; /** * @dev maps keccak256 hash to string representation */ mapping(bytes32 => string) internal _roleNames; string public constant DELEGATED_ADMIN = "DELEGATED_ADMIN"; bytes32 private constant _DELEGATED_ADMIN_HASH = keccak256(abi.encodePacked(DELEGATED_ADMIN)); /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. */ modifier onlyRole(address delegate, string memory role) { _checkRole(delegate, role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IDelegatedAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc IDelegatedAccessControl */ function hasRole( address delegate, string memory role, address account ) external view returns (bool) { return _hasRole(delegate, role, account); } /** * @inheritdoc IDelegatedAccessControl */ function checkRole( address delegate, string memory role, address account ) external view { _checkRole(delegate, role, account); } /** * @inheritdoc IDelegatedAccessControl */ function getRoleAdmin(address delegate, string memory role) public view returns (string memory) { return _roleNames[_roles[delegate][_hash(role)].adminRole]; } /** * @inheritdoc IDelegatedAccessControl */ function grantRole( address delegate, string memory role, address account ) external onlyRole(delegate, getRoleAdmin(delegate, role)) { _grantRole(delegate, role, account); } /** * @inheritdoc IDelegatedAccessControl */ function revokeRole( address delegate, string memory role, address account ) external onlyRole(delegate, getRoleAdmin(delegate, role)) { _revokeRole(delegate, role, account); } /** * @inheritdoc IDelegatedAccessControl */ function renounceRole( address delegate, string memory role, address account ) external { require(account == _msgSender(), "DelegatedAccessControl: can only renounce roles for self"); _revokeRole(delegate, role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin( address delegate, string memory role, string memory adminRole ) internal virtual { string memory previousAdminRole = getRoleAdmin(delegate, role); bytes32 adminRoleHash = _hash(adminRole); _roleNames[adminRoleHash] = adminRole; _roles[delegate][_hash(role)].adminRole = adminRoleHash; emit RoleAdminChanged(delegate, role, previousAdminRole, adminRole); } function _hasRole( address delegate, string memory role, address account ) internal view returns (bool) { return _roles[delegate][_hash(role)].members[account]; } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. */ function _grantRole( address delegate, string memory role, address account ) internal virtual { if (_hasRole(delegate, role, account)) return; bytes32 hashedRole = _hash(role); _roleNames[hashedRole] = role; _roles[delegate][hashedRole].members[account] = true; emit RoleGranted(delegate, role, account, _msgSender()); } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. */ function _revokeRole( address delegate, string memory role, address account ) internal virtual { if (!_hasRole(delegate, role, account)) return; _roles[delegate][_hash(role)].members[account] = false; emit RoleRevoked(delegate, role, account, _msgSender()); } /** * @dev Revert with a standard message if `_msgSender()` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(address delegate, string memory role) internal view virtual { _checkRole(delegate, role, _msgSender()); } /** * @dev Revert with a standard message if `account` is missing `role`. */ function _checkRole( address delegate, string memory role, address account ) internal view virtual { if (!_hasRole(delegate, role, account)) { revert( string( abi.encodePacked( "DelegatedAccessControl: account ", StringsUpgradeable.toHexString(uint160(account), 20), " is missing role ", role ) ) ); } } function _hash(string memory str) internal pure returns (bytes32) { bytes32 hashed = keccak256(abi.encodePacked(str)); if (hashed == _DELEGATED_ADMIN_HASH) return 0x00; return hashed; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[48] private __gap; }