Skip to main content

What is Namehash?

Namehash is the algorithm used by ENS to convert human-readable domain names (like alice.eth) into unique, fixed-size identifiers (bytes32 hashes) that are used throughout the ENS smart contracts.
ENS smart contracts never work with strings directly. All operations use namehash values, which are deterministic 256-bit hashes.

Why Namehash?

Namehash provides several critical properties:
  1. Fixed Size - All names, regardless of length, produce a 32-byte hash
  2. Deterministic - The same name always produces the same hash
  3. Hierarchical - Parent/child relationships are preserved
  4. Efficient - Operations on hashes are cheaper than string operations
  5. Privacy - Subdomain hashes don’t reveal parent domain names

The Algorithm

Namehash is a recursive algorithm defined in EIP-137.

Definition

namehash([]) = 0x0000000000000000000000000000000000000000000000000000000000000000
namehash([label, ...]) = keccak256(namehash(...), keccak256(label))

Step-by-Step Process

  1. Start with the empty root: 0x0000...0000 (32 zero bytes)
  2. For each label (from right to left):
    • Hash the label with keccak256
    • Concatenate the parent hash with the label hash
    • Hash the result with keccak256
  3. Return the final hash

Visual Breakdown

Examples

Example 1: Computing namehash(‘eth’)

const { keccak256, toUtf8Bytes, concat, hexlify } = require('ethers');

// Step 1: Start with root (empty)
const root = '0x0000000000000000000000000000000000000000000000000000000000000000';

// Step 2: Hash the label 'eth'
const labelHash = keccak256(toUtf8Bytes('eth'));
// labelHash = 0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0

// Step 3: Concatenate root + labelHash and hash the result
const ethNode = keccak256(concat([root, labelHash]));
// ethNode = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae
The .eth TLD always resolves to 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae

Example 2: Computing namehash(‘alice.eth’)

// Start with the hash we computed for 'eth'
const ethNode = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae';

// Hash the label 'alice'
const aliceLabelHash = keccak256(toUtf8Bytes('alice'));
// aliceLabelHash = 0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec

// Concatenate ethNode + aliceLabelHash and hash
const aliceEthNode = keccak256(concat([ethNode, aliceLabelHash]));
// aliceEthNode = 0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec

Example 3: Computing namehash(‘pay.alice.eth’)

// Start with 'alice.eth' hash from above
const aliceEthNode = '0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec';

// Hash the label 'pay'
const payLabelHash = keccak256(toUtf8Bytes('pay'));

// Concatenate and hash
const payAliceEthNode = keccak256(concat([aliceEthNode, payLabelHash]));

Implementation in Solidity

You can see namehash in action in the ENS registry:
function setSubnodeOwner(
    bytes32 node,
    bytes32 label,
    address owner
) public virtual override authorised(node) returns (bytes32) {
    bytes32 subnode = keccak256(abi.encodePacked(node, label));
    _setOwner(subnode, owner);
    emit NewOwner(node, label, owner);
    return subnode;
}
This function computes the namehash of a subdomain by:
  1. Taking the parent node hash
  2. Taking the label hash (already hashed)
  3. Computing keccak256(node || label) using abi.encodePacked
The label parameter is already the keccak256 hash of the label string, not the string itself. To create pay.alice.eth, you would pass:
  • node: namehash of alice.eth
  • label: keccak256(“pay”)

Working with Labels vs Nodes

Labels

A label is:
  • A single component of a domain name (e.g., “alice” in “alice.eth”)
  • Stored as keccak256(label_string) when passed to contracts
  • Used in setSubnodeOwner to create subdomains
// To create subdomain 'pay.alice.eth':
bytes32 labelHash = keccak256(abi.encodePacked("pay"));
bytes32 aliceEthNode = namehash("alice.eth");

registry.setSubnodeOwner(aliceEthNode, labelHash, owner);

Nodes

A node is:
  • The complete namehash of a full domain name
  • Used for all registry queries and operations
  • A 32-byte hash representing the entire name hierarchy
// Query operations use the full node hash
bytes32 node = namehash("alice.eth");

address owner = registry.owner(node);
address resolver = registry.resolver(node);

Normalization

Before computing namehash, names should be normalized according to UTS-46:
Unicode allows multiple ways to represent the same character. Normalization ensures:
  • Alice.eth and alice.eth produce the same hash
  • Confusable characters (lookalikes) are handled consistently
  • Names are in a canonical form
  • Convert to lowercase
  • Apply Unicode normalization (NFC)
  • Disallow certain characters (emoji, control characters)
  • Map lookalike characters to canonical forms
const { normalize } = require('@ensdomains/eth-ens-namehash');

normalize('Alice.eth')  // → 'alice.eth'
normalize('ALICE.eth')  // → 'alice.eth'
normalize('alice.eth')  // → 'alice.eth'
Always normalize names before computing namehash. Most ENS libraries do this automatically, but if you’re implementing namehash yourself, normalization is critical.

Complete Implementation Example

Here’s a complete JavaScript implementation:
const { keccak256, toUtf8Bytes, concat } = require('ethers');
const { normalize } = require('@ensdomains/eth-ens-namehash');

function namehash(name) {
  // Normalize the name first
  const normalizedName = normalize(name);
  
  // Start with the root hash (all zeros)
  let node = '0x0000000000000000000000000000000000000000000000000000000000000000';
  
  // If empty name, return root
  if (normalizedName === '') {
    return node;
  }
  
  // Split name into labels and process from right to left
  const labels = normalizedName.split('.');
  
  for (let i = labels.length - 1; i >= 0; i--) {
    const labelHash = keccak256(toUtf8Bytes(labels[i]));
    node = keccak256(concat([node, labelHash]));
  }
  
  return node;
}

// Usage
console.log(namehash(''))           // 0x0000...0000
console.log(namehash('eth'))        // 0x93cdeb708b75...
console.log(namehash('alice.eth'))  // 0x787192fc5378...

Using ENS Libraries

In practice, you should use established ENS libraries rather than implementing namehash yourself:
import { namehash } from 'ethers';

const hash = namehash('alice.eth');
// 0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec

Testing Namehash

You can verify your namehash implementation against known values:
NameNamehash
“ (empty)0x0000000000000000000000000000000000000000000000000000000000000000
eth0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae
foo.eth0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f
alice.eth0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec
These test vectors are useful for verifying your namehash implementation is correct.

Common Pitfalls

Forgetting to normalize: Always normalize names before hashing. Alice.eth and alice.eth should produce the same hash.
Wrong direction: Process labels from right to left (TLD first), not left to right.
String vs Hash confusion: Registry functions expect node hashes (bytes32), not strings. Always namehash first.
Label hashing: When calling setSubnodeOwner, the label parameter should be keccak256(labelString), not the raw string.

Privacy Implications

Namehash has interesting privacy properties:
// Given only the hash of 'pay.alice.eth', you cannot determine:
// 1. The parent domain ('alice.eth')
// 2. The subdomain label ('pay')
// 3. The full name

const hash = '0x...'  // namehash of 'pay.alice.eth'
// There's no way to reverse this to get the original name
Namehash is one-way. You cannot derive the original name from the hash. This provides some privacy for subdomain structures, though names can often be discovered through events or off-chain data.

Next Steps

Architecture Overview

Understand how namehash fits into the overall ENS architecture

Registry Contracts

See how to use namehash values with the ENS registry

Build docs developers (and LLMs) love