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

Universal Resolver V2

The Universal Resolver V2 is the primary public entry point for ENS name resolution. It resolves names by traversing the hierarchical registry tree, walking from the root registry down through subregistries to locate the correct resolver for any name. It also provides functions for navigating and verifying the registry hierarchy itself.

Architecture

UniversalResolverV2 extends AbstractUniversalResolver, a shared base contract that implements all resolution logic: forward resolution, reverse resolution, CCIP-Read gateway batching, and callback chaining. The only function the base leaves abstract is findResolver(), which each version overrides with its own registry traversal strategy:

  • URv1 walks a flat ENS registry
  • URv2 walks the hierarchical registry tree via getSubregistry() at each level

This means resolve(), reverse(), and all CCIP-Read infrastructure work identically across both versions. The LibRegistry library that powers the hierarchy traversal is also used directly by other contracts including DNSAliasResolver, DNSTLDResolver, and ENSV2Resolver.

Registry Navigation

ENSv2 adds functions for navigating and verifying the registry hierarchy. All walk down from root except findCanonicalName which walks up via getParent(), and findCanonicalRegistry which does both.

findResolver is the core function that powers resolve() and reverse() internally. It walks down the registry tree, following getSubregistry() at each label, and returns the resolver covering the longest matching suffix of the name. See Resolution for a step-by-step walkthrough.

findExactRegistry and findRegistries locate registries by walking down from root. Use findExactRegistry when you need a single registry and trust the hierarchy; use findRegistries to get the full ancestry:

  • findRegistries("sub.nick.eth") returns [<sub>, <nick>, <eth>, <root>]
  • If sub.nick.eth has no subregistry: [address(0), <nick>, <eth>, <root>]

findCanonicalName reconstructs a registry's DNS-encoded name by walking up via getParent(), verifying each link. Returns empty bytes if any registry in the chain hasn't called setParent(). findCanonicalRegistry combines both directions: it walks down to find the registry, then up to verify the name matches, ensuring no aliasing or stale references are exploited.

Verifying Name Ownership

In ENSv1, verifying name ownership was a single call: check ownerOf(namehash) on one contract. In ENSv2, a name like sub.nick.eth is a chain of entries across multiple registries (see Registry Hierarchy), so verification requires confirming the full chain from root to the name's registry.

findCanonicalRegistry is designed for this purpose. To verify that a specific account owns a name:

  1. Call findCanonicalRegistry(name) to get the verified registry
  2. If non-zero, query the registry for the name's token ownership

The bidirectional check ensures that:

  • The registry is actually reachable from the root (downward walk)
  • The registry considers itself to be at that position in the hierarchy (upward walk via getParent())
  • No aliasing or stale references are being exploited

Reference

View Functions

resolve(name, data)Forward-resolve a single record for a DNS-encoded name. For batch resolution, encode data as multicall(bytes[]).
resolveWithGateways(name, data, gateways)Same as resolve, but with custom CCIP-Read gateway URLs.
resolveWithResolver(resolver, name, data, gateways)Resolve using a specific resolver address, bypassing the findResolver lookup.
reverse(lookupAddress, coinType)Reverse resolution per ENSIP-19: look up the primary name for an address, then verify via forward resolution.
reverseWithGateways(lookupAddress, coinType, gateways)Same as reverse, but with custom CCIP-Read gateway URLs.
findResolver(name)Find the resolver for a name by walking down the registry hierarchy.
findExactRegistry(name)Find the registry at a name's position by walking down from root.
findRegistries(name)Return all registries in a name's ancestry, innermost first.
findCanonicalName(registry)Reconstruct a registry's DNS-encoded name by walking up via getParent().
findCanonicalRegistry(name)Find the registry for a name, verified canonical via bidirectional walk.

Constants

ROOT_REGISTRYThe ENSv2 root registry. All hierarchy traversal starts from this address.
batchGatewayProviderDefault gateway provider for CCIP-Read batching.

Errors

ResolverNotFound(name)No resolver found for the name.
ResolverNotContract(name, resolver)Resolver address has no deployed code.
UnsupportedResolverProfile(selector)Resolver doesn't support the requested function.
ResolverError(errorData)Resolver reverted during resolution.
ReverseAddressMismatch(primary, primaryAddress)Forward resolution of the primary name doesn't match the original address.
HttpError(status, message)HTTP error from a CCIP-Read gateway.