Skip to main content

Overview

The IRegistry interface is the foundational contract interface for ENS v2 registry functionality. It extends IERC1155Singleton to provide ERC1155-compliant token management for domain names while adding registry-specific features for subdomains, resolvers, and parent-child relationships. Interface Selector: 0x51f67f40

Inheritance

interface IRegistry is IERC1155Singleton
  • Inherits from IERC1155Singleton for ERC1155 token functionality
  • Adds ownerOf(uint256 id) for single-owner token queries

Events

NameRegistered

Emitted when a subdomain is registered.
event NameRegistered(
    uint256 indexed tokenId,
    bytes32 indexed labelHash,
    string label,
    address owner,
    uint64 expiry,
    address indexed sender
);
tokenId
uint256
required
The unique token ID for the registered name.
labelHash
bytes32
required
Keccak256 hash of the label.
label
string
required
The human-readable label that was registered.
owner
address
required
The address that owns the registered name.
expiry
uint64
required
The expiration timestamp for the registration.
sender
address
required
The address that initiated the registration.
Example:
// Listening for name registrations
registry.on("NameRegistered", (tokenId, labelHash, label, owner, expiry, sender) => {
    console.log(`Name ${label} registered with token ID ${tokenId}`);
    console.log(`Owner: ${owner}, Expires: ${expiry}`);
});

NameReserved

Emitted when a subdomain is reserved.
event NameReserved(
    uint256 indexed tokenId,
    bytes32 indexed labelHash,
    string label,
    uint64 expiry,
    address indexed sender
);
tokenId
uint256
required
The unique token ID for the reserved name.
labelHash
bytes32
required
Keccak256 hash of the label.
label
string
required
The human-readable label that was reserved.
expiry
uint64
required
The expiration timestamp for the reservation.
sender
address
required
The address that reserved the name.
Example:
// Reserve a name without assigning ownership
IPermissionedRegistry(registry).reserve("premium", block.timestamp + 365 days);
// Emits: NameReserved(tokenId, keccak256("premium"), "premium", expiry, msg.sender)

NameUnregistered

Emitted when a subdomain is unregistered.
event NameUnregistered(uint256 indexed tokenId, address indexed sender);
tokenId
uint256
required
The token ID of the unregistered name.
sender
address
required
The address that initiated the unregistration.
Example:
// Unregister a name
IStandardRegistry(registry).unregister(tokenId);
// Emits: NameUnregistered(tokenId, msg.sender)

ExpiryUpdated

Emitted when a name’s expiry is changed.
event ExpiryUpdated(uint256 indexed tokenId, uint64 newExpiry, address indexed sender);
tokenId
uint256
required
The token ID of the name being renewed.
newExpiry
uint64
required
The new expiration timestamp.
sender
address
required
The address that updated the expiry.
Example:
// Renew a name for another year
uint64 newExpiry = uint64(block.timestamp + 365 days);
IStandardRegistry(registry).renew(tokenId, newExpiry);
// Emits: ExpiryUpdated(tokenId, newExpiry, msg.sender)

SubregistryUpdated

Emitted when a subdomain’s subregistry is changed.
event SubregistryUpdated(
    uint256 indexed tokenId,
    IRegistry subregistry,
    address indexed sender
);
tokenId
uint256
required
The token ID of the name.
subregistry
IRegistry
required
The new subregistry contract address. Can be address(0) to remove subregistry.
sender
address
required
The address that updated the subregistry.
Example:
// Set a subregistry for a name
IStandardRegistry(registry).setSubregistry(tokenId, IRegistry(subregistryAddress));
// Emits: SubregistryUpdated(tokenId, subregistryAddress, msg.sender)

// Remove subregistry
IStandardRegistry(registry).setSubregistry(tokenId, IRegistry(address(0)));
// Emits: SubregistryUpdated(tokenId, address(0), msg.sender)

ResolverUpdated

Emitted when a name’s resolver is changed.
event ResolverUpdated(uint256 indexed tokenId, address resolver, address indexed sender);
tokenId
uint256
required
The token ID of the name.
resolver
address
required
The new resolver contract address. Can be address(0) to remove resolver.
sender
address
required
The address that updated the resolver.
Example:
// Set a resolver for a name
IStandardRegistry(registry).setResolver(tokenId, resolverAddress);
// Emits: ResolverUpdated(tokenId, resolverAddress, msg.sender)

// Remove resolver
IStandardRegistry(registry).setResolver(tokenId, address(0));
// Emits: ResolverUpdated(tokenId, address(0), msg.sender)

TokenRegenerated

Emitted when a token is regenerated with a new token ID.
event TokenRegenerated(uint256 indexed oldTokenId, uint256 indexed newTokenId);
oldTokenId
uint256
required
The previous token ID.
newTokenId
uint256
required
The new token ID after regeneration.
Token regeneration occurs when roles are granted or revoked to maintain ERC1155 compliance. The token ID encodes role information, so role changes require a new token ID.
Example:
// Granting roles may trigger token regeneration
IPermissionedRegistry(registry).grantRoles(resource, roleBitmap, account);
// May emit: TokenRegenerated(oldTokenId, newTokenId)

ParentUpdated

Emitted when the registry’s parent is changed.
event ParentUpdated(IRegistry indexed parent, string label, address indexed sender);
parent
IRegistry
required
The new parent registry contract.
label
string
required
The label of this registry under the parent.
sender
address
required
The address that updated the parent.
Example:
// Set the canonical parent for this registry
IStandardRegistry(registry).setParent(IRegistry(parentRegistry), "subdomain");
// Emits: ParentUpdated(parentRegistry, "subdomain", msg.sender)

Functions

getSubregistry

Fetches the registry for a subdomain.
function getSubregistry(string calldata label) external view returns (IRegistry);
query.label
string
required
The label to resolve.
return
IRegistry
The address of the registry for this subdomain, or address(0) if none exists.
Example:
// Get the subregistry for a label
IRegistry subregistry = registry.getSubregistry("wallet");

if (address(subregistry) != address(0)) {
    // Subregistry exists, can query further subdomains
    address resolver = subregistry.getResolver("alice");
}

getResolver

Fetches the resolver responsible for the specified label.
function getResolver(string calldata label) external view returns (address);
query.label
string
required
The label to fetch a resolver for.
return
address
The address of a resolver responsible for this name, or address(0) if none exists.
Example:
// Get the resolver for a label
address resolver = registry.getResolver("alice");

if (resolver != address(0)) {
    // Resolver exists, can query records
    bytes memory addr = IResolver(resolver).resolve(
        abi.encodeWithSignature("addr(bytes32)", node)
    );
}

getParent

Get canonical location of this registry.
function getParent() external view returns (IRegistry parent, string memory label);
parent
IRegistry
The canonical parent registry of this registry.
label
string
The canonical label of this registry under its parent.
Example:
// Get the parent information
(IRegistry parent, string memory label) = registry.getParent();

console.log("This registry is registered as", label, "under parent", address(parent));

// Build full domain path
if (address(parent) != address(0)) {
    (IRegistry grandparent, string memory parentLabel) = parent.getParent();
    // Domain path: label.parentLabel.eth
}

Usage Examples

Query domain information

import {IRegistry} from "./interfaces/IRegistry.sol";

contract DomainQuery {
    IRegistry public registry;

    constructor(address _registry) {
        registry = IRegistry(_registry);
    }

    function queryDomain(string calldata label) external view returns (
        address resolver,
        IRegistry subregistry,
        IRegistry parent,
        string memory parentLabel
    ) {
        // Get resolver and subregistry
        resolver = registry.getResolver(label);
        subregistry = registry.getSubregistry(label);

        // Get parent information
        (parent, parentLabel) = registry.getParent();

        return (resolver, subregistry, parent, parentLabel);
    }
}

Listen to registry events

import {IRegistry} from "./interfaces/IRegistry.sol";

contract RegistryMonitor {
    IRegistry public registry;

    event DomainActivity(string action, uint256 tokenId, address user);

    constructor(address _registry) {
        registry = IRegistry(_registry);
    }

    function monitorRegistry() external {
        // In practice, use event filters with web3/ethers
        // This pseudocode demonstrates the concept

        registry.on("NameRegistered", (tokenId, labelHash, label, owner, expiry, sender) => {
            emit DomainActivity("registered", tokenId, owner);
        });

        registry.on("NameUnregistered", (tokenId, sender) => {
            emit DomainActivity("unregistered", tokenId, sender);
        });

        registry.on("ExpiryUpdated", (tokenId, newExpiry, sender) => {
            emit DomainActivity("renewed", tokenId, sender);
        });
    }
}
import {IRegistry} from "./interfaces/IRegistry.sol";

contract RegistryNavigator {
    // Resolve a multi-level domain path
    function resolvePath(IRegistry rootRegistry, string[] calldata labels)
        external
        view
        returns (address finalResolver)
    {
        IRegistry currentRegistry = rootRegistry;

        for (uint256 i = 0; i < labels.length; i++) {
            if (i == labels.length - 1) {
                // Last label, get its resolver
                finalResolver = currentRegistry.getResolver(labels[i]);
            } else {
                // Intermediate label, navigate to subregistry
                currentRegistry = currentRegistry.getSubregistry(labels[i]);
                require(
                    address(currentRegistry) != address(0),
                    "Subregistry not found"
                );
            }
        }

        return finalResolver;
    }

    // Build full domain path from root
    function buildFullPath(IRegistry registry)
        external
        view
        returns (string[] memory path)
    {
        // Count depth
        uint256 depth = 0;
        IRegistry current = registry;
        while (address(current) != address(0)) {
            depth++;
            (IRegistry parent,) = current.getParent();
            current = parent;
        }

        // Build path array
        path = new string[](depth);
        current = registry;
        for (uint256 i = depth; i > 0; i--) {
            (IRegistry parent, string memory label) = current.getParent();
            path[i - 1] = label;
            current = parent;
        }

        return path;
    }
}

IPermissionedRegistry

Registry with role-based access control.

ERC1155Singleton

Base ERC1155 contract with single-owner functionality.
The IRegistry interface provides read-only queries for registry state. For registration and modification operations, use IPermissionedRegistry.

Build docs developers (and LLMs) love