Open-source project
ensdomains/ens-contracts avatar
ensdomains/ens-contracts

ENS Contracts: The Solidity Core Behind .eth Name Resolution

The core contracts of the ENS protocol. DNSResolver = Experimental support is available for hosting DNS domains on the Ethereum blockchain via ENS.

729 stars582 forksTypeScriptMIT

At a glance

What is it?
The ensdomains/ens-contracts repository holds the registry, registrar, controller and resolver contracts that the ENS protocol runs on, plus a compiled npm package for projects that only need the ABIs. It is infrastructure for Solidity developers, not an end-user product.
Who is it for?
Adopt ensdomains/ens-contracts if you are integrating ENS resolution or building a registrar on top of the registry, and you want the audited reference implementation rather than a reimplementation. Do not adopt it if you only need to read a name in a frontend: use a library that speaks to deployed contracts instead of importing Solidity.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 15 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the ENS contracts actually solve

Ethereum addresses are not memorable, and a name system that maps them has to answer a narrow question: given a name, who owns it and which contract resolves it? The ENS registry is that answer. The README describes it as "the core contract that lies at the heart of ENS resolution" and says all ENS lookups start by querying it. The registry keeps a list of domains and records the owner, resolver and TTL for each, and it lets a domain owner change that data.

The audience is narrower than the ENS brand suggests. This repository is for Solidity developers who need the registry interface, the .eth registrar, the controller that prices and registers names, or a resolver that stores records. The README also notes that the repository doubles as an npm package with compiled JSON contracts, which serves a second audience: JavaScript and TypeScript projects that want the ABIs and bytecode without running a Solidity compiler. If you are looking for a wallet or a place to buy a name, this is not the layer you touch.

Registry, registrar and controller: how ownership is split

The design separates who owns a name from how names are handed out, and that split is the most interesting decision in the codebase. BaseRegistrar owns the TLD in the ENS registry and implements a deliberately small set of powers: the registrar owner may add and remove controllers, controllers may register new domains and extend expiry, and they cannot change ownership or reduce an expiration time. Name owners may transfer ownership and may reclaim ownership in the registry if they have lost it.

EthRegistrarController sits on the other side of that boundary. It registers new names through a commit and reveal process, and the README states the reason directly: the minimum delay and expiry for commitments exist to prevent miners or other users from frontrunning registrations. In practice a user first commits to a hash whose preimage contains the name and a secret, then after the minimum delay and before the commitment expires calls register with the name and secret. Pricing is not fixed in the controller. A price oracle contract is set by the registrar owner, and StablePriceOracle prices by name length against a fiat currency oracle while SimplePriceOracle returns a fixed price per domain per year.

The trade-off is visible once you look at what controllers cannot do. Because a controller can extend expiry but not shorten it or move ownership, a compromised or replaced controller cannot strip names from their holders. That is a strong guarantee for name owners and a real constraint for anyone who wants a controller to do something more aggressive, such as reclaiming names for non-payment.

Installing the npm package and registering a name

The README gives the package import first. If your project only needs the compiled artifacts, install the package and import the contract objects by name. The README shows this exact import list, which includes BaseRegistrar, ENS, ENSRegistry, ETHRegistrarController, PublicResolver and others.

js
import {
  BaseRegistrar,
  ENSRegistry,
  ETHRegistrarController,
  PublicResolver,
} from '@ensdomains/ens-contracts'

If you are writing Solidity instead, the README imports the source files by path. The registry files live under contracts/registry and the .eth registrar files under contracts/ethregistrar.

solidity
import '@ensdomains/ens-contracts/contracts/registry/ENS.sol';
import '@ensdomains/ens-contracts/contracts/registry/ENSRegistry.sol';
import '@ensdomains/ens-contracts/contracts/ethregistrar/BaseRegistrar.sol';

The README also covers environments without a compiler. In that case the raw hardhat artifacts are available inside the installed package at the path shown below, where modName and contractName are substituted for the module and contract you want.

bash
node_modules/@ensdomains/ens-contracts/artifacts/contracts/${modName}/${contractName}.sol/${contractName}.json

To work on the contracts themselves, the package scripts in package.json define the loop. Compilation goes through hardhat, and the test script compiles first and then runs vitest.

bash
bun run compile
bun run test

The README's setup section begins with a git clone and is truncated in the published text, so read the repository's own instructions for the rest of that sequence rather than assuming flags that are not shown. One genuine first-use caveat: the npm package exposes contract objects and artifacts, not a ready client. You still supply a provider, an address for the deployed registry on your target network, and the ABI wiring yourself.

Resolvers, and where DNSResolver stops being production-ready

PublicResolver is the general-purpose resolver, and the README lists the profiles it composes by EIP: ABIResolver for EIP 205, AddrResolver for EIP 137 and EIP 2304 multicoin support, ContentHashResolver for EIP 1577, InterfaceResolver for EIP 165, NameResolver for EIP 181 reverse resolution, PubkeyResolver for EIP 619, TextResolver for EIP 634, and DataResolver for ENSIP-24 arbitrary data. Each profile is a separate piece of the contract surface, so a name can carry a text record, a content hash and a multicoin address without those concerns bleeding into one another.

DNSResolver is the one to treat with care. The repository description itself labels it "Experimental support is available for hosting DNS domains on the Ethereum blockchain via ENS", and the README repeats the word experimental and points at an older ENS document for detail. Experimental is not a synonym for broken, but it does mean the interface and the deployment story can move, and the README does not document a rollback path for a DNS name that has been brought on chain. If your integration depends on DNS names resolving through ENS, that gap is the thing to resolve before you commit to it.

Reverse resolution is the other corner worth naming. ReverseRegistrar manages it through the .addr.reverse special-purpose TLD, and NameResolver implements the name() call. Reverse records are a separate registration from the forward name, which surprises people who assume that owning a name gives them a matching reverse entry.

Where this is the wrong tool

Two cases stand out. The first is reading names in a user-facing application. Importing Solidity interfaces or artifact JSON into a frontend gives you no caching, no name normalization and no handling of the many ways a lookup can fail. The npm package is a source of ABIs, not a resolution client, and the README says nothing about retries, fallbacks or cache invalidation.

The second is the .test registrar. TestRegistrar is described as facilitating easy testing of ENS on Ethereum test networks, providing functionality to instantly claim a domain for test purposes, expiring 28 days after it was claimed. The README states it is currently deployed on Ropsten. Ropsten is a proof-of-work testnet that Ethereum retired in 2022, so a deployment note pointing at it is a sign that this part of the documentation has not kept pace with the networks developers actually use. Treat TestRegistrar as a local or test-harness tool and confirm the network yourself before wiring anything to a public testnet.

There is also a licensing-adjacent point about the repository structure: the package publishes build output, contracts, artifacts and deployments. Pinning a version matters more than usual here because the artifacts you import are generated, and a version bump can change both the Solidity source and the compiled JSON in the same step.

How this differs from using a resolver library

The practical alternative for most teams is a client-side resolution library such as viem or ethers, which talks to deployed ENS contracts over RPC. The difference is not quality, it is layer. A library gives you a function that takes a name and returns an address, and it handles the registry hop, the resolver call and the encoding for you. This repository gives you the contracts those libraries call, which matters when you are deploying your own registrar, writing a resolver, or building something that has to run on chain.

Choosing between them is mostly a question of where your logic lives. If your logic is in a browser or a server process, the library is the shorter path and the deployed contracts are already there. If your logic must be enforced by the EVM, for example a contract that issues subdomains or a resolver that stores records under its own rules, you need the interfaces and the reference implementations here. The README's FIFSRegistrar is a good illustration of the second case: a first-in-first-served registrar that issues subdomains to the first account to request them is a few lines of policy that only makes sense if it runs on chain.

Maintenance, upgrades and the licence

The repository is not archived, and the last push was on 2026-03-13, which is the same date as the v1.7.0 release. Before that, v1.6.0 landed on 2025-09-10 and v1.5.2 on 2025-06-10, so the release cadence over the past year has been roughly two to three tagged versions. The default branch is staging, not main, which is worth knowing when you build tooling that assumes a main branch or when you point a dependency at a branch rather than a tag.

Upgrade cost is dominated by the fact that these are deployed contracts. Changing a registry or a registrar implementation is a deployment and a migration, not a package bump. The README documents one such migration for the registry, the 2020 ENS Registry Migration, and the repository ships ENSRegistryWithFallback as the implementation that followed it. That history is the clearest signal of what an upgrade looks like in this codebase: a new deployment plus a compatibility contract, with the old one still reachable for a period.

The licence is MIT, stated in the repository metadata and shipped as LICENSE.txt at the top level. MIT is permissive and does not impose copyleft obligations on your own code, but it also carries no warranty, and the contracts handle funds and name ownership. Read the licence text in the repository rather than relying on a summary, and treat the audit note as scoped: the README says the EthRegistrar contracts were audited by ConsenSys Diligence, with a link to the 2019 report. That audit covers the .eth registrar contracts as they stood then, not every contract in the repository today.

Editorial conclusion

Adopt ensdomains/ens-contracts if you are integrating ENS resolution or building a registrar on top of the registry, and you want the audited reference implementation rather than a reimplementation. Do not adopt it if you only need to read a name in a frontend: use a library that speaks to deployed contracts instead of importing Solidity. Before you write anything, verify which network and which deployed registry you are targeting, and confirm that v1.7.0 is the version you pin, since the package version in package.json tracks the release tag. The boundary worth remembering is the one the README states plainly: DNSResolver is experimental.

Frequently asked questions

What does ENS stand for in ensdomains/ens-contracts?

The repository does not expand the acronym in the README. It describes ENS as a system with documentation at docs.ens.domains and a homepage at ens.domains, and the contracts themselves are the registry, registrar and resolver layer of that system.

How do I install ens-contracts in a project?

The README shows the package published as @ensdomains/ens-contracts, imported by contract name, for example BaseRegistrar or ENSRegistry. For Solidity you import the source paths under contracts/registry and contracts/ethregistrar, and if your environment has no compiler you can read the hardhat artifacts inside the installed package.

Which contracts are in the ens-contracts list?

The README groups them into registry contracts (ENS.sol, ENSRegistry, ENSRegistryWithFallback, FIFSRegistrar, ReverseRegistrar, TestRegistrar), EthRegistrar contracts (BaseRegistrar, EthRegistrarController, SimplePriceOracle, StablePriceOracle) and resolvers, with PublicResolver composing profiles such as ABIResolver, AddrResolver, ContentHashResolver, NameResolver, PubkeyResolver and TextResolver.

Is the ENS registry in ens-contracts the contract all lookups start from?

Yes. The README states that the ENS registry is the core contract at the heart of ENS resolution and that all ENS lookups start by querying it, with the registry recording the owner, resolver and TTL for each domain.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ensdomains-ens-contracts.svg)](https://hysenlabs.com/projects/ensdomains-ens-contracts)