Skip to main content
Restrictions on names are handled by fuses. Fuses are labels that can be irreversibly added to a name until that name expires. Fuses are split into two categories: owner-controlled fuses and parent-controlled fuses. Owner-controlled fuses can be set by either the owner of the name or the owner of the parent name, but parent-controlled fuses can only be set by the owner of the parent name. When a fuse is set, we say it is “burned”, to illustrate the one-way nature of fuses. Fuses cannot be unset, and will only reset if the name’s expiry is less than the current block.timestamp.

Special Control Fuses

There are three fuses that have special control over the burning mechanism: PARENT_CANNOT_CONTROL, CANNOT_UNWRAP, and CANNOT_BURN_FUSES.

PARENT_CANNOT_CONTROL

  • PARENT_CANNOT_CONTROL cannot be burned unless CANNOT_UNWRAP is burned on the parent name. Burning PARENT_CANNOT_CONTROL moves the name to the Emancipated state.
  • Burning PARENT_CANNOT_CONTROL prevents the owner of the parent name from burning any further fuses on the name.
  • Burning PARENT_CANNOT_CONTROL ensures the parent cannot call setSubnodeOwner() or setSubnodeRecord() on that specific subdomain. This means that a parent cannot change the owner or records of a subdomain using setSubnode*() and additionally a parent cannot burn fuses or extend with setSubnode*() and setChildFuses(), since the functions are now restricted.
  • Burning PARENT_CANNOT_CONTROL allows the parent or name owner to burn owner-controlled fuses. Until PARENT_CANNOT_CONTROL is burnt, no owner-controlled fuses can be set. This does not affect parent-controlled fuses.
  • A parent name’s owner can still extend the expiry of a name with PARENT_CANNOT_CONTROL burned.
Important note for developers: To ensure PARENT_CANNOT_CONTROL is actually set and not automatically reset to 0, the expiry MUST be extended at the same time to be greater than the current block’s timestamp.

CANNOT_UNWRAP

  • CANNOT_UNWRAP cannot be burned unless PARENT_CANNOT_CONTROL is also burned. Burning CANNOT_UNWRAP moves the name to the Locked state.
  • Burning CANNOT_UNWRAP ensures the name cannot be unwrapped.
  • Other user-controlled fuses cannot be burned unless CANNOT_UNWRAP is burned.
  • Other user-controlled fuses can only be burned if CANNOT_UNWRAP is also burned. This also allows the owner of the name to burn PARENT_CANNOT_CONTROL and all parent-controlled fuses on subdomains.

CANNOT_BURN_FUSES

  • CANNOT_BURN_FUSES can be burned to prevent any further changes to fuses. As with all user-controlled fuses, this cannot be burned unless PARENT_CANNOT_CONTROL and CANNOT_UNWRAP are both burned.

Owner-Controlled Fuses

These fuses can be burned by the owner of a name or by the owner of the parent name (if the name is not emancipated). Fuse bits 1-16 (the first uint16 of the uint32) are owner-controlled fuses.

CANNOT_UNWRAP = 1

If this fuse is burned, the name cannot be unwrapped, and calls to unwrap and unwrapETH2LD, as well as other effects that would unwrap a name such as setSubnodeOwner will fail.

CANNOT_BURN_FUSES = 2

If this fuse is burned, no further fuses can be burned. This has the effect of ‘locking open’ some set of permissions on the name. Calls to setFuses, and other methods that modify the set of fuses, will fail. Other methods can still be called successfully so long as they do not specify new fuses to burn.

CANNOT_TRANSFER = 4

If this fuse is burned, the name cannot be transferred. Calls to safeTransferFrom and safeBatchTransferFrom will fail.

CANNOT_SET_RESOLVER = 8

If this fuse is burned, the resolver cannot be changed. Calls to setResolver, setRecord and setSubnodeRecord will fail.

CANNOT_SET_TTL = 16

If this fuse is burned, the TTL cannot be changed. Calls to setTTL, setRecord, and setSubnodeRecord will fail.

CANNOT_CREATE_SUBDOMAIN = 32

If this fuse is burned, new subdomains cannot be created. Calls to setSubnodeOwner and setSubnodeRecord will fail if they reference a name that does not already exist.

CANNOT_APPROVE = 64

If this fuse is burned, approve() cannot be called on this name anymore and so the current approved address cannot be changed until expiry.

Parent-Controlled Fuses

These fuses can only be burned by the owner of the parent name. Fuse bits 17-32 (the second half of the uint32) are parent-controlled fuses.

PARENT_CANNOT_CONTROL = 65536

If this fuse is burned, existing subdomains cannot be replaced by the parent name and the parent can no longer burn other fuses on this child. Calls to setSubnodeOwner and setSubnodeRecord will fail if they reference a name that already exists. Attempting to burn fuses in setChildFuses will also fail.
This fuse can only be burnt by the parent of a node.

IS_DOT_ETH = 131072

If this fuse is burned, it means that the name is a .eth name.
This fuse cannot be burned manually and is burned when wrapETH2LD() or onERC721Received() is called.

CAN_EXTEND_EXPIRY = 262144

If this fuse is burned, a name will be able to extend its own expiry in the NameWrapper.
Does not apply to .eth 2LDs, as the expiry will inherit from the registrar in that case, and this fuse will not be burned when wrapping/registering .eth 2LDs.

Custom Fuses

Anything that is not predefined as a fuse can be burnt as a custom fuse:
  • Fuse bits 1-16 (the first uint16 of the uint32) can be burnt by the owner of the name, or by the owner of the parent name at the same time as burning PARENT_CANNOT_CONTROL.
  • Fuse bits 17-32 (the second half of the uint32) can only be burnt by the owner of the parent name.

Fuse Constants

uint32 constant CANNOT_UNWRAP = 1;
uint32 constant CANNOT_BURN_FUSES = 2;
uint32 constant CANNOT_TRANSFER = 4;
uint32 constant CANNOT_SET_RESOLVER = 8;
uint32 constant CANNOT_SET_TTL = 16;
uint32 constant CANNOT_CREATE_SUBDOMAIN = 32;
uint32 constant CANNOT_APPROVE = 64;
uint32 constant PARENT_CANNOT_CONTROL = 1 << 16;  // 65536
uint32 constant IS_DOT_ETH = 1 << 17;              // 131072
uint32 constant CAN_EXTEND_EXPIRY = 1 << 18;       // 262144
uint32 constant CAN_DO_EVERYTHING = 0;
uint32 constant PARENT_CONTROLLED_FUSES = 0xFFFF0000;
uint32 constant USER_SETTABLE_FUSES = 0xFFFDFFFF;  // all fuses apart from IS_DOT_ETH

Checking Fuses

Using allFusesBurned()

To check whether or not a fuse is burnt you can use this function that takes a fuse mask of all fuses you want to check.
const areBurned = await allFusesBurned(
  namehash('vitalik.eth'),
  CANNOT_TRANSFER | CANNOT_SET_RESOLVER,
)
// if CANNOT_UNWRAP AND CANNOT_SET_RESOLVER are *both* burned this will return true

Using getData()

getData() gets the owner, fuses and also the expiry of the name. The fuses it returns will be a uint32 and you will have to decode this yourself. If you just need to check a fuse has been burned, you can call allFusesBurned as it will use less gas.
const [owner, fuses, expiry] = await getData(namehash('vitalik.eth'));

// Check if a specific fuse is burned
if (fuses & CANNOT_TRANSFER) {
  console.log('Cannot transfer this name');
}

Fuse Burning Rules

The following rules must be observed when burning fuses:
  1. CANNOT_UNWRAP cannot be burned unless PARENT_CANNOT_CONTROL is also burned
  2. Other user-controlled fuses cannot be burned unless CANNOT_UNWRAP is burned
  3. PARENT_CANNOT_CONTROL cannot be burned unless CANNOT_UNWRAP is burned on the parent name
  4. If CANNOT_BURN_FUSES is burned, no further fuses can be burned
  5. Once a name is Emancipated (PCC burned), the parent can no longer burn fuses on it

Build docs developers (and LLMs) love