ENS Logo
Docs

ETH Registrar

The ETH Registrar is a special registrar. It allows for trustless on-chain name registration and is in charge of the ".eth" TLD.

.com.xyz.nl.net.org.shop.photos.pizza.cash.money.news.info.gold.domains.social.de.city.lol.rip.company.es.network.me.us.id.fr.space.ninja.tools.wtf.capital.finance.vision.limo.link.uk.world.dev.day.fyi.cooland any other DNSSEC-compatible domain...

BaseRegistrar vs Controller

The ETH Registrar is split into two contracts. The BaseRegistrar and the ETHRegistrarController. The BaseRegistrar is responsible for name ownership, transfers, etc (ownership related), while the Controller is responsible for registration & renewal (pricing related). This separation is done to reduce the attack surface of the registrar, and provides users with the guarantees of continued ownership of a name so long as the registrar is in place.

Controllers

The ETHRegistrarController is the main controller for the ETH Registrar, and provides a straightforward registration and renewal mechanism.

Pricing Structure

The ETH Registrar charges a fee for registration. This fee is paid in ETH and is set to prevent spamming the registrar. Any protocol fees are sent to the ENS Treasury.

Pricing Oracle

Initially, a single pricing oracle was deployed, the StablePriceOracle. This contract has owner-set prices for each name length (1, 2, 3, 4, 5 or more). Users do not have to interact with this oracle directly, as the controller provides functionality to determine the pricing for a registration or renewal.

3, 4, and 5 Letter Names

The ETH Registrar has special pricing for 3, 4, and 5 (and more) letter names. At the time of writing, a 5+ letter .eth will cost you 5 USD per year. A 4 letter 160 USD per year, and a 3 letter 640 USD per year. This pricing structure is done to promote market diversity as there are an exponentially less amount of names the shorter they become. The minimum length of a name is 3 characters.

Name LengthPrice (USD)
5+5
4160
3640

Premium & Auctions

In addition to length-based pricing the ETH Registrar also has a premium pricing structure. 90 days after a name expires (aka after the grace period), the name will go into a Temporary Premium Auction. The Auction is a 21 day dutch auction, meaning that the price starts high (~100 Million USD) and exponentially decrease till it hits 0 or a bid goes through.

This is done to prevent sniping of names, and ensures the name goes to the highest bidder fairly.

You can read more about the temporary premium in this article.

Where does the money go?

Upon registration funds are sent to the ETHRegistrarController. The controller then sends the funds to the ENS Treasury (anyone can call the withdraw method to trigger this). Income from the ETH Registrar is used to fund the development of ENS, its ecosystem, and other public goods.

Read more about our spending in Article III of the Constitution.

ERC721 and NFTs

In the early days of ENS, the ERC721 standard did not exist. The original ETH Registrar formed the pre-cursor to the ERC721 standard. As we witnessed the ERC721 being standardized support for it was added to the ETH Registrar.

Today, users can interact with the ETH Registrar to transfer their name just like with any other ERC721 token.

Registering a Name

To register a name you can use the ENS Manager App, ENS Fairy, your favourite mobile wallet (if supported), or any other frontend you like. If you would like to register a name through a smart contract, or your own interface, you can use the following functions.

For the process of .eth name registration the ETH Registrar uses a two transaction commit reveal process.

Commit
Wait
Reveal

Commit Reveal

The ETHRegistrarController implements a commit reveal scheme to prevent frontrunning. The way it works is that during the registration process we first call the commit function with an opaque bit of data (the commitmenthash). Wait a few blocks and then call the register function.

The commit function takes a commitment hash, which can be generated using the makeCommitment function. The commitment hash is opaque and revealed during the register function.

The commit reveal process ensures no eavesdropping third-party is able to register your name before you can.

ETHRegistrarController.makeCommitment(name string, owner address, duration uint256, secret bytes32, resolver address, data bytes[], reverseRecord bool, ownerControlledFuses uint16)

// For example
makeCommitment(
    "myname", // "myname.eth" but only the label
    0x1234..., // The address you want to own the name
    31536000, // 1 year (in seconds)
    0x1234..., // A secret that you have generated (32 bytes)
    0x1234..., // The address of the resolver you want to use
    [],
    false, // Set as primary name?
    0
);

Once you have calculated the commitment hash you can call the commit function.

ETHRegistrarController.commit(commitment bytes32)

Note this does require an on-chain transaction. After having committed it is recommended to wait at least the MIN_COMMITMENT_AGE (~60 seconds) before registering.

Registering

Once you have committed you can register your name. Registration takes in the same parameters as the makeCommitment function, but this time is in the form of a transaction.

Before initiating registration ensure that:

  • available(name) == true
  • duration >= MIN_REGISTRATION_DURATION
  • commitments[commitment] is between 1 min and 24 hrs old
  • msg.value >= rentPrice(name, duration) + 5-10% (slippage)

Because the rent price may vary over time, callers are recommended to send slightly more than the value returned by rentPrice, a premium of 5-10% will likely be sufficient. Any excess funds sent during registration are automatically returned to the caller.

ETHRegistrarController.register(name string, owner address, duration uint256, secret bytes32, resolver address, data bytes[], reverseRecord bool, ownerControlledFuses uint16)

// For example
register(
    "myname", // "myname.eth" but only the label
    0x1234..., // The address you want to own the name
    31536000, // 1 year (in seconds)
    0x1234..., // A secret that you have generated (32 bytes)
    0x1234..., // The address of the resolver you want to use
    [],
    false, // Set as primary name?
    0
);

If you would like to try registering a name live on a testnet you can use the live demo below.

Register a name
Connect Wallet

Renewing a Name

ETHRegistrarController.renew()

Any user can renew a domain, not just the owner. This means that if you want to ensure a name doesn't expire you can renew it for someone.

By allowing renewal for any arbitrary amount of time users can ensure their name will not expire. As per the separation between registry and controller, even with upgraded controller your name will still be yours.

Renew a name
Connect Wallet

Other features

ETHRegistrarController.MIN_COMMITMENT_AGE uint
ETHRegistrarController.MAX_COMMITMENT_AGE uint
ETHRegistrarController.MIN_REGISTRATION_DURATION uint
// Get Commitment Timestamp
ETHRegistrarController.commitments mapping(bytes32=>uint)
// Get Rent Price
ETHRegistrarController.rentPrice(string name, uint duration) view returns (uint)
// Check Name Validity
ETHRegistrarController.valid(string name) view returns (bool)
// Check Name Availability
// Returns true if the name is both valid and available for registration by this controller.
ETHRegistrarController.available(string name) view returns (bool)
// Calculate Commitment Hash
ETHRegistrarController.makeCommitment(string name, address owner, uint256 duration, bytes32 secret, address resolver, bytes[] data, bool reverseRecord, uint16 ownerControlledFuses) view returns (bytes32)

// Get Name Expiry (unix timestamp at which registration expires)
BaseRegistrar.nameExpires(uint256 label) view returns (uint)
// Check Name Availability (less specific, use ETHRegistrarController.available instead)
BaseRegistrar.available(uint256 label) view returns (bool)
// Get Transfer Period End (unix timestamp at which transfer period (from legacy registrar) ends)
BaseRegistrar.transferPeriodEnds uint
// Get Controller Status
BaseRegistrar.controllers mapping(address=>bool)
// Check Token Approval
BaseRegistrar.getApproved(uint256 tokenId) view returns (address operator)
// Check All Tokens Approval
BaseRegistrar.isApprovedForAll(address owner, address operator) view returns (bool)
// Get Token Owner
BaseRegistrar.ownerOf(uint256 tokenId) view returns (address)
// Get Token URI
BaseRegistrar.tokenURI(uint256 tokenId) view returns (string)

Writable

// Transfer a Name
BaseRegistrar.transferFrom(address from, address to, uint256 tokenId)
BaseRegistrar.safeTransferFrom(address from, address to, uint256 tokenId)
BaseRegistrar.safeTransferFrom(address from, address to, uint256 tokenId, bytes _data)
// Approve Operator
BaseRegistrar.approve(address to, uint256 tokenId)
// Set Approval For All
BaseRegistrar.setApprovalForAll(address operator, bool approved)
// Reclaim ENS Record
BaseRegistrar.reclaim(uint256 label)

Events

// BaseRegistrar
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event NameMigrated(uint256 indexed hash, address indexed owner, uint expires);
event NameRegistered(uint256 indexed hash, address indexed owner, uint expires);
event NameRenewed(uint256 indexed hash, uint expires);

// Controller
event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint cost, uint expires);
event NameRenewed(string name, bytes32 indexed label, uint cost, uint expires);
Contributors
Last Modified
2 days ago