Cee, five, ef, dee, ef, four, ow, six, seven, oh wait, it’s ow, seven, six, bee… That’s of course not the way you would like to share your Ethereum address or Swarm and IPFS content hash. You copy and send it or scan QR codes but this experience is still inferior to using easy to remember, readable names. In the same way, as DNS solved this problem for IP addresses, ENS has a goal of mitigating this issue in Ethereum ecosystem.

ENS stands for Ethereum Name Service which is a set of smart contracts that provide distributed naming system on the Ethereum blockchain. ENS itself is not a part of Ethereum stack but a community-driven specification that you can easily extend using your custom resolvers for owned names.

In this tutorial, I would like to guide you through the process of registering a test domain on Rinkeby. You can easily apply following steps to other testnets changing only addresses.

Unlike on Mainnet or Ropsten, ENS on Rinkeby does not support .eth domain and is limited to use a .test domain only. Registering .test domain is much quicker as it does not require you to go through the auction process. Test domain also expires after 28 days which makes it good enough to use during a development phase.

Register .test domain

Before we start, let’s download ensutils-testnet.js which contains a few ABIs and helper functions that will make the process more straightforward. Hardcoded ENS address is no good for us since we would like to use Rinkeby, not Ropsten. On the other hand, if you try to register a name on Ropsten, you are all set. Head toward line 220 and change ENS contract address.

var ens = ensContract.at('0xe7410170f87102df0055eb195163a03b7f2bff4a');

I have prepared a gist with all changes to ensutils needed to make it work on Rinkeby: ensutils-rinkeby.js.

  • ENS on Mainnet: 0x314159265dd8dbb310642f98f50c066173c1259b
  • ENS on Ropsten: 0x112234455c3a32fd11230c42e7bccd4a84e02010
  • ENS on Rinkeby: 0xe7410170f87102df0055eb195163a03b7f2bff4a

Once you did it, connect to running Ethereum node using geth and load the ensutils script.

$ geth attach
> loadScript("./ensutils-rinkeby.js")

Before we register, let’s check whether the name you would like to own is available.

> testRegistrar.expiryTimes(web3.sha3("michalzalecki"))

If the timestamp returned is 0, or from the past then you can register this name.

> testRegistrar.register(web3.sha3("michalzalecki"), eth.accounts[0], {from: eth.accounts[0]})

Wait for the blockchain to include transaction. You can now confirm the registration by rechecking the expiration time and the owner.

> testRegistrar.expiryTimes(web3.sha3("michalzalecki"))
> ens.owner(namehash("michalzalecki.test"))

Congratulations, you have got yourself a name on ENS!

Public resolver

You own a name, but it does not resolve to anything just yet. You need a resolver. From what I know, there is no official public resolver on Rinkeby at the time of writing. The good news is that anyone can create and deploy one. If you don’t want to create a resolver, then skip to the next section.

Clone the ENS repository, install dependencies and remove the build directory.

git clone https://github.com/ethereum/ens
npm install
rm -rf build

Change truffle config so you can deploy to Rinkeby.

// truffle.js
module.exports = {
  networks: {
    rinkeby: {
      host: "",
      port: 8545,
      network_id: 4
  solc: {
    optimizer: {
      enabled: true,
      runs: 200,

We do not need to deploy all contracts but only PublicResolver. Let’s remove unnecessary migration file and create a new one.

rm migrations/2_deploy_contracts.js
// 2_public_resolver.js
const PublicResolver = artifacts.require("./PublicResolver.sol");

const ENS = "0xe7410170f87102df0055eb195163a03b7f2bff4a";

module.exports = function(deployer) {
  deployer.deploy(PublicResolver, ENS);

The last step is to run a migration.

./node_modules/.bin/truffle migrate --network rinkeby

If you want to know more about verifying your contract on Etherscan and give your resolver a little credibility, then read my other tutorial: How to verify smart contract on Etherscan?

Resolving domains

We need to point our name to the resolver and set an address to which our domain is resolving. If you do not have your resolver, you can use mine.

> publicResolver = resolverContract.at("0x5d20cf83cb385e06d2f2a892f9322cd4933eacdc")
> ens.setResolver(namehash("michalzalecki.test"), publicResolver.address, {from: eth.accounts[0]})

You can now check that ENS keeps the resolver address for your domain

> ens.resolver(namehash("michalzalecki.test"))

I am going to set the domain to point to my account address, but it could be any address at this point.

> publicResolver.setAddr(namehash("michalzalecki.test"), eth.accounts[0], {from: eth.accounts[0]})

Now let’s see to which address our domain resolves. We can do it by directly calling public resolver or using the getAddr helper function.

> getAddr("michalzalecki.test")
> publicResolver.addr(namehash("michalzalecki.test"))

If you would like to resolve addresses on the client or your oracle, then you already have all tools you need. You can use ABI of the resolver and helper function from ensutils. There is also ethereum-ens but it does not work with web3 1.0.


ENS is a great project that tries to make the Ethereum userland more friendly place. While developer experience is not great yet, it is a strong foundation and broad adoption of the popular projects like MyEtherWallet, MetaMask, or Mist.

There is of course more to ENS than what we have covered here. There is a proper documentation available that answers many questions and provides multiple examples. If you would like to learn more about the project and people behind it grab a cup of coffee and watch Nick Johnson’s talk - Ethereum ENS - The Ethereum Name Service.