// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "./IDelegatedAccessControlEnumerable.sol"; import "./DelegatedAccessControl.sol"; /** * @dev Extension of {DelegatedAccessControl} that allows enumerating the members of each role. */ // solhint-disable ordering, max-line-length abstract contract DelegatedAccessControlEnumerable is IDelegatedAccessControlEnumerable, DelegatedAccessControl { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.Bytes32Set; // solhint-disable-next-line func-name-mixedcase, no-empty-blocks function __DelegatedAccessControlEnumerable_init() internal onlyInitializing { __DelegatedAccessControl_init_unchained(); __DelegatedAccessControlEnumerable_init_unchained(); } // solhint-disable-next-line func-name-mixedcase, no-empty-blocks function __DelegatedAccessControlEnumerable_init_unchained() internal onlyInitializing {} /** * @dev delegate to role members mapping */ mapping(address => mapping(bytes32 => EnumerableSetUpgradeable.AddressSet)) private _roleMembers; /** * @dev maps account to set of delegates */ mapping(address => EnumerableSetUpgradeable.AddressSet) private _accountDelegates; /** * @dev maps account to delegate to set of roles */ mapping(address => mapping(address => EnumerableSetUpgradeable.Bytes32Set)) private _accountDelegateRoles; /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IDelegatedAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember}, {getRoleMemberCount}, {getDelegates} and {getDelegateRoles} make sure * you perform all queries on the same block. See the following * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ function getRoleMember( address delegate, string calldata role, uint256 index ) external view override returns (address) { return _roleMembers[delegate][_hash(role)].at(index); } /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(address delegate, string memory role) public view override returns (uint256) { return _roleMembers[delegate][_hash(role)].length(); } function getDelegates( address account, uint256 offset, uint256 limit ) external view returns (address[] memory delegates, uint256 total) { EnumerableSetUpgradeable.AddressSet storage delegateSet = _accountDelegates[account]; total = delegateSet.length(); if (offset >= total) return (new address[](0), total); if (limit > total - offset) { limit = total - offset; } delegates = new address[](limit); for (uint256 i = 0; i < limit; i++) { delegates[i] = delegateSet.at(offset + i); } } function getDelegateRoles( address delegate, address account, uint256 offset, uint256 limit ) external view returns (string[] memory roles, uint256 total) { EnumerableSetUpgradeable.Bytes32Set storage roleSet = _accountDelegateRoles[account][delegate]; total = roleSet.length(); if (offset >= total) return (new string[](0), total); if (limit > total - offset) { limit = total - offset; } roles = new string[](limit); for (uint256 i = 0; i < limit; i++) { roles[i] = _roleNames[roleSet.at(offset + i)]; } } /** * @dev Overload {_grantRole} to track enumerable memberships */ function _grantRole( address delegate, string memory role, address account ) internal virtual override { super._grantRole(delegate, role, account); bytes32 hashedRole = _hash(role); _roleMembers[delegate][hashedRole].add(account); _accountDelegates[account].add(delegate); _accountDelegateRoles[account][delegate].add(hashedRole); } /** * @dev Overload {_revokeRole} to track enumerable memberships */ function _revokeRole( address delegate, string memory role, address account ) internal virtual override { super._revokeRole(delegate, role, account); bytes32 hashedRole = _hash(role); _roleMembers[delegate][hashedRole].remove(account); _accountDelegates[account].remove(delegate); _accountDelegateRoles[account][delegate].remove(hashedRole); } /** * @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[47] private __gap; }