The IEnhancedAccessControl interface provides a sophisticated role-based access control system for ENS v2. It supports resource-based roles, assignee counting, root resource override, and efficient bitmap-based role management.Interface Selector:0x8f452d62
function ROOT_RESOURCE() external view returns (uint256);
The ROOT_RESOURCE is a special resource ID that acts as a global override. Roles granted in the root resource apply to all other resources.Example:
// Grant admin role in root resource (applies everywhere)uint256 rootResource = registry.ROOT_RESOURCE();registry.grantRootRoles(ADMIN_ROLE, adminAddress);// Admin can now manage any resourceassert(registry.hasRoles(anyResource, ADMIN_ROLE, adminAddress) == true);
Role bitmaps encode multiple roles in a single uint256. Each bit position represents a different role. For example, 0x03 means roles at positions 0 and 1 are granted.
import {MANAGER_ROLE, EDITOR_ROLE} from "./libraries/RegistryRolesLib.sol";// Grant single roleregistry.grantRoles(resource, MANAGER_ROLE, managerAddress);// Grant multiple roles using bitmap ORuint256 multipleRoles = MANAGER_ROLE | EDITOR_ROLE;registry.grantRoles(resource, multipleRoles, operatorAddress);// Check resultbool success = registry.grantRoles(resource, EDITOR_ROLE, editorAddress);require(success, "Failed to grant roles");
Root roles are powerful - they apply to all resources. Use sparingly for global administrators.
Example:
import {ADMIN_ROLE} from "./libraries/RegistryRolesLib.sol";// Grant global admin roleregistry.grantRootRoles(ADMIN_ROLE, globalAdminAddress);// Admin can now manage any resourcebool canManageAny = registry.hasRoles( anyResource, ADMIN_ROLE, globalAdminAddress);assert(canManageAny == true);
A bitmap encoding the count of assignees for each role.
The role count bitmap packs assignee counts for all roles. Each role uses 4 bits to store counts from 0-15.
Example:
// Get role count bitmapuint256 counts = registry.roleCount(resource);// Extract count for a specific role (implementation detail)// This is typically done internally, not by external callers
Returns true if the account has ALL specified roles in the root resource.
Example:
// Check if account is a global adminif (registry.hasRootRoles(ADMIN_ROLE, account)) { // Account is a global administrator // Can perform admin operations on any resource}
Returns true if ANY of the roles has at least one assignee.
Example:
// Check if there are any managersif (registry.hasAssignees(resource, MANAGER_ROLE)) { console.log("Resource has at least one manager");} else { console.log("Resource has no managers - need to assign one");}// Check if there are any managers or editorsif (registry.hasAssignees(resource, MANAGER_ROLE | EDITOR_ROLE)) { console.log("Resource has managers and/or editors");}
This function returns packed data. The counts are encoded with 4 bits per role (0-15 assignees).
Example:
// Get assignee counts(uint256 counts, uint256 mask) = registry.getAssigneeCount( resource, MANAGER_ROLE | EDITOR_ROLE);// The exact unpacking depends on role positions// Typically used internally or by specialized tools
import {IEnhancedAccessControl} from "./interfaces/IEnhancedAccessControl.sol";import {MANAGER_ROLE, EDITOR_ROLE} from "./libraries/RegistryRolesLib.sol";contract PermissionChecker { IEnhancedAccessControl public eac; constructor(address _eac) { eac = IEnhancedAccessControl(_eac); } modifier onlyRole(uint256 resource, uint256 roleBitmap) { require( eac.hasRoles(resource, roleBitmap, msg.sender), "Missing required role" ); _; } modifier onlyRootRole(uint256 roleBitmap) { require( eac.hasRootRoles(roleBitmap, msg.sender), "Missing required root role" ); _; } function managerOnlyAction(uint256 resource) external onlyRole(resource, MANAGER_ROLE) { // Only accounts with manager role can call this } function editorAction(uint256 resource) external onlyRole(resource, EDITOR_ROLE) { // Only accounts with editor role can call this } function adminAction() external onlyRootRole(MANAGER_ROLE) { // Only global administrators can call this }}
import {IEnhancedAccessControl} from "./interfaces/IEnhancedAccessControl.sol";import {MANAGER_ROLE} from "./libraries/RegistryRolesLib.sol";contract AssigneeTracker { IEnhancedAccessControl public eac; constructor(address _eac) { eac = IEnhancedAccessControl(_eac); } function ensureManager(uint256 resource) external view returns (bool) { return eac.hasAssignees(resource, MANAGER_ROLE); } function safeGrantManager( uint256 resource, address newManager ) external { // Ensure there's at least one manager before revoking require( eac.hasAssignees(resource, MANAGER_ROLE), "Cannot revoke last manager" ); eac.grantRoles(resource, MANAGER_ROLE, newManager); } function safeRevokeManager( uint256 resource, address manager ) external { // Check if there are multiple managers bool hasManagers = eac.hasAssignees(resource, MANAGER_ROLE); require(hasManagers, "No managers to revoke"); // Revoke the role eac.revokeRoles(resource, MANAGER_ROLE, manager); // Ensure at least one manager remains require( eac.hasAssignees(resource, MANAGER_ROLE), "Cannot remove last manager" ); }}
The Enhanced Access Control system uses bitmap-based role management for gas efficiency. Each role is represented by a bit in a uint256, allowing up to 32 different roles per resource.