Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Enhanced Access Control

Enhanced Access Control (EAC) is the permission system used throughout ENSv2. It controls who is allowed to do what, and on which names. EAC supports up to 2^256 independent resources (e.g., individual names), 64 roles per resource (32 regular + 32 admin), and up to 15 accounts per role.

Think of it like a building where each room has its own set of locks: you can give someone a key to one room, or a master key that opens every room. EAC works the same way, but with ENS names and on-chain permissions.

Resources

A resource is the thing you're controlling access to. In most ENS contracts, a resource is a name - but it can be any uint256 identifier that makes sense for the contract.

Each ENSv2 contract defines how its resources are computed. For example, registries derive resources from a name's labelhash, while resolvers derive resources from a namehash and record type. See How Contracts Use EAC for the specific schemes.

There's also a special resource called ROOT_RESOURCE (0x0) that represents the contract itself. Permissions granted on ROOT_RESOURCE apply everywhere - like a master key. If you have a role on ROOT_RESOURCE, you automatically have that role on every individual resource too.

Roles

A role represents a specific permission - for example, "can set the resolver" or "can register subnames". Each ENS contract defines the roles that are relevant to it.

Roles are always tied to a resource. Granting someone the "set resolver" role on alice.eth doesn't let them set the resolver on bob.eth. To give someone a permission across all names, grant the role on ROOT_RESOURCE instead.

Up to 15 accounts can hold the same role on the same resource, enabling shared management and delegation.

How role checks work: when a contract checks whether an account has a role on a specific name, it looks in two places - the name itself and ROOT_RESOURCE - and allows the action if the role is found in either. This is how the master key effect works.

Admin Roles

Every role has a corresponding admin role that controls who can manage it. If you hold the admin role, you can:

  • Grant the regular role to other accounts
  • Grant the admin role itself to other accounts
  • Revoke either role from other accounts

For example, the admin role for "set resolver" controls who is allowed to grant or revoke the "set resolver" permission. Admin roles follow the same resource-scoping - you can be an admin for a specific name or for all names via ROOT_RESOURCE.

Granting and Revoking

Roles are managed through four functions:

GrantRevokeScope
grantRolesrevokeRolesSpecific resource (name)
grantRootRolesrevokeRootRolesROOT_RESOURCE (contract-wide)

The caller must hold the admin role for every role being granted or revoked. Admin role holders can also revoke the admin role itself, including from themselves.

As a safety guardrail, grantRoles and revokeRoles reject ROOT_RESOURCE. You must use grantRootRoles / revokeRootRoles explicitly for contract-wide permissions. This prevents accidental global grants.

All four functions return true if the account's roles actually changed, or false if the roles were already in the desired state.

Callback Hooks

EAC provides four internal hooks that contracts can override to customize permission behavior:

Role Change Callbacks

  • _onRolesGranted(resource, account, oldRoles, newRoles, roleBitmap): called after roles are successfully granted. Receives the account's role bitmap before and after the change, plus the specific roles that were newly added.
  • _onRolesRevoked(resource, account, oldRoles, newRoles, roleBitmap): called after roles are successfully revoked. Same parameters, with roleBitmap containing the roles that were actually removed.

Both are no-ops in the base implementation. The Permissioned Registry overrides these to regenerate ERC1155 tokens when roles change, invalidating stale transfer approvals.

Grant/Revoke Restriction Hooks

  • _getSettableRoles(resource, account): returns which roles the account is allowed to grant on a given resource. By default, an account can grant any role for which it holds the corresponding admin role. Contracts override this to impose additional restrictions.
  • _getRevokableRoles(resource, account): returns which roles the account is allowed to revoke on a given resource. Same default behavior as settable roles.

The Permissioned Registry overrides both to prevent admin role escalation on individual names after registration and to block all role changes on unregistered or reserved names.

Bitmap Layout

Under the hood, roles are packed into a single uint256 bitmap split into two halves. Each role occupies one nybble (4 bits), giving space for up to 32 regular roles and 32 corresponding admin roles:

  255         128 127            0
  ┌──────────────┬───────────────┐
  │ Admin Roles  │ Regular Roles │
  └──────────────┴───────────────┘
  63           32 31             0  ← nybble indices

Each nybble is a 4-bit slot. A regular role at nybble index N occupies bits 4N to 4N + 3, so nybble 0 is bits 0–3, nybble 1 is bits 4–7, and so on. Its admin counterpart sits at the same position in the upper half (4N + 128 to 4N + 131).

EAC tracks role assignments in two mappings:

  • _roles[resource][account]: stores which roles a given account holds on a given resource. Each nybble is either 0 (no role) or 1 (has role).
  • _roleCount[resource]: stores how many accounts hold each role on a given resource. Each nybble is a count from 0 to 15, using the same nybble-per-role layout. This is why the maximum number of assignees per role is 15: it's the largest value a 4-bit nybble can store.

Replacing Fuses

EAC replaces the one-way fuse system from the Name Wrapper. The key conceptual shift: in ENSv1, you burned permissions to restrict what could be done. In ENSv2, you revoke roles instead. Both achieve the same end result, but revoking is reversible if you hold the admin role.

FeatureENSv1 FusesENSv2 EAC
RevocabilityOne-way burn — permanentFully reversible grant/revoke
DelegationSingle owner onlyUp to 15 accounts per role per resource
ScopePer-name onlyPer-name or contract-wide via ROOT_RESOURCE
ExtensibilityFixed set of 16 fusesEach contract defines its own roles (up to 32)
Transfer controlCANNOT_TRANSFER fuseROLE_CAN_TRANSFER_ADMIN (revoke to make non-transferable)
Resolver controlCANNOT_SET_RESOLVER fuseROLE_SET_RESOLVER (revoke to lock resolver)
Subdomain controlCANNOT_CREATE_SUBDOMAIN fuseROLE_REGISTRAR (revoke to prevent new subnames)

How Contracts Use EAC

Each ENSv2 contract defines its own roles and its own resource scheme. See the EAC Permissions section on each contract page for details:

Reference

Write Functions

grantRoles(resource, roleBitmap, account)Grant roles on a specific resource. Reverts if resource is ROOT_RESOURCE.
revokeRoles(resource, roleBitmap, account)Revoke roles on a specific resource. Reverts if resource is ROOT_RESOURCE.
grantRootRoles(roleBitmap, account)Grant roles on ROOT_RESOURCE (contract-wide).
revokeRootRoles(roleBitmap, account)Revoke roles on ROOT_RESOURCE (contract-wide).

View Functions

roles(resource, account)Get the full role bitmap for an account on a resource.
roleCount(resource)Get the assignee count bitmap for a resource (nybble per role).
hasRoles(resource, roleBitmap, account)Check whether an account has all specified roles. Checks both the resource and ROOT_RESOURCE.
hasRootRoles(roleBitmap, account)Check whether an account has all specified roles on ROOT_RESOURCE only.
hasAssignees(resource, roleBitmap)Check whether any accounts hold the specified roles on a resource.
getAssigneeCount(resource, roleBitmap)Get per-role assignee counts for the specified roles.

Constants

ROOT_RESOURCEThe root resource constant (0). Roles on ROOT_RESOURCE apply to all resources.

Events

EACRolesChanged(resource, account, oldRoleBitmap, newRoleBitmap)Roles granted or revoked for an account on a resource.

For code examples of granting and revoking roles, see the Permissioned Registry and Permissioned Resolver pages.