Technology11 minute read

The Ultimate ENS and ĐApp Tutorial

The Ethereum Name Service is a blockchain-backed alternative to the internet's traditional Domain Name System. If you own a .eth domain, you can deploy smart contracts that let users set up their own subdomains—but then, of course, you'll want a corresponding ĐApp to make it user-friendly. In this tutorial, Toptal Freelance Ethereum Developer Radek Ostrowski provides complete coverage of this scenario, this time with a focus on ĐApp development.
The Ethereum Name Service is a blockchain-backed alternative to the internet's traditional Domain Name System. If you own a .eth domain, you can deploy smart contracts that let users set up their own subdomains—but then, of course, you'll want a corresponding ĐApp to make it user-friendly. In this tutorial, Toptal Freelance Ethereum Developer Radek Ostrowski provides complete coverage of this scenario, this time with a focus on ĐApp development.

In the previous article in the blockchain series, I introduced you to smart contract development but skimmed over how to develop a distributed application (dapp, stylized ĐApp). This time, I want to focus on precisely that. However, to make our work more meaningful, let’s build a service that allows people to create free Ethereum subdomains with a single blockchain transaction.

Let’s start this ĐApp tutorial by explaining the Ethereum Name Service (ENS) first.

The Ethereum Name Service

ENS is a blockchain equivalent of the commonly used Domain Name System (DNS). Both of them could be described with a phone-book metaphor. They serve as a lookup service, translating human-readable names into their underlying representations—in the case of DNS and ENS, computer addresses instead of telephone numbers.

DNS translates the domain names of website addresses into Internet Protocol (IP) addresses. They’re easily understood by computers, but not so convenient for us humans. For example, at the time of this writing, represents—which would you rather remember and type?

$ ping
PING ( 56 data bytes

ENS serves the same purpose, but instead of IP addresses, domains are mapped into Ethereum addresses, which are 42 characters long. The top-level ENS domain is .eth.

Interestingly, the equivalent of DNS’ Central Registry is represented as set of smart contracts. These are running on the Ethereum blockchain, in a distributed manner, “… meaning it doesn’t suffer from the insecurity of the DNS system. You can be confident names you enter work the way their owner intended,” as the ENS website describes.

Below is a high-level overview of the ENS architecture. The backbone of the service is the ENS registry smart contract, which maps representations of domain names to ENS resolvers’ smart contracts. Those, in turn, map to the target Ethereum address. Looking at the diagram, if an application wanted to resolve the ENS address radek.freedomain.eth, it would first query the ENS Registry for the corresponding ENS Resolver and then ask the resolver for the mapping Ethereum address which would respond with 0x0987.... For more information dig into the ENS Documentation here.

.eth ENS Domains

ENS launched on May 4th, 2017, and at the moment (phase one), its domains have to contain seven or more characters. It is not possible to use toptal.eth which has six characters, so we will instead use an ENS domain that I bought: freedomain.eth.

Anyone can register a .eth domain name by locking in some ether in a Vickrey auction process mediated by deployed smart contracts. The auction consists of two stages, bid and reveal, lasting three and two days respectively.

First, an auction for a domain must be started along with placing the first bid. Once it’s running, anyone can place their bids. Once the first stage completes, new bids are no longer accepted and the participants then have to reveal their bids. The winner with the highest bid only has to match the second highest bid, and the rest of the ether is refunded. All the remaining participants also get a refund. But it’s important to note that if you do not reveal your bid in time, the ether you bid is lost.

Once you are confirmed the winner, you only have to finalize the auction and set the resolver and the target address. Simple? Maybe not so much, if you are new to Ethereum. There are several tools that can help you with that—like MyCrypto, MyEtherWallet, and ENS Manager—but it’s a multistage process nonetheless.

What about subdomains? Are they any simpler? Let’s have a look.

ENS Subdomains

What use is there of a subdomain in the first place? My best guess is that it’s the same as with subdomains in DNS. They can also give more of an organizational feel to users, where the domain represents the organization and the subdomain resembles the structure of email addresses: => radek.freedomain.eth

A novel use case currently being implemented by Tenzorum Project is to use your subdomain as a username/login to Ethereum ĐApps. Imagine a case where there is a ĐApp game which you’ve been playing for a while. It would be great to continue playing the game with the same login from your laptop and your mobile phone as you are on the go even though both devices use different key pairs. This is how this is achieved:

  • Log in to the ĐApp using your subdomain, e.g., radek.tenz-id.eth
  • The ĐApp asks you to sign a message to verify you are the holder of the corresponding private key
  • The ĐApp checks that you are authorized to log in by asking your personal wallet smart contract. (You guessed it, the login represented by the ENS subdomain is pointing to your personal wallet’s address.)

This has the added benefit of being able to continue playing even if you lose your device (and the private key on it), as you won’t lose the access to your personal wallet.

Subdomains map names to addresses exactly the same way as domains do, but you don’t need to go through an auction process to create one. Luckily, there is no minimum length limit on ENS subdomains, either.

To register a subdomain, you have to be the owner of the domain as a prerequisite. To create or update a subdomain, you have to call setSubnodeOwner on the ENS registry smart contract. Once successful, the same logic as with domains applies: The owner has to set the resolver and the target address.

What if we could automate this and simplify the process for users? I have created [a smart contract that does exactly that—the aforementioned EnsSubdomainFactory.sol—allowing anyone to create a subdomain with a single blockchain call. However, as the focus of this article is on building ĐApps, I will leave it for you to examine as an exercise.

Distributed Applications (ĐApps)

A ĐApp is simply a front-end website interacting with the blockchain back end. The website must be viewed on a Ethereum-enabled browser like Mist, Parity, or the mobile app Cipher; or by installing the MetaMask plugin.

Instead of a traditional back end with a server and database, the read and write calls are made directly to the Ethereum blockchain. The calls can be done using the JSON RPC protocol, but luckily for us there is a library which wraps the calls in a developer-friendly fashion.

Meet Web3. There are several versions in different programming languages, but we are going to use the JavaScript one, web3.js. Also, the web3 instance is being injected by the browser, or in the case of our approach here, the MetaMask plugin.

HTML Website

So now, we can get down to how to build a ĐApp. In our case, all we need is a simple form with five elements:

  • Input field for subdomain name
  • Dropdown with available domains
  • Input field for the new owner of the subdomain
  • Input field for the target address
  • Submit button

I’ve pimped it up a bit with Bootstrap. (Remember, in terms of UI features, a ĐApp is like any other web app, so we don’t need a ĐApp framework per se—regular web tech like Bootstrap does perfectly well here.) Here’s what it looks like:

It’s just a regular HTML website so we will not elaborate more. All the magic happens in the JavaScript file dapp.js.

JavaScript and Web3

To build any generic ĐApp, what you need is:

  • A Web3 instance (e.g., with MetaMask provider)
  • A way to read from the blockchain (e.g., ethereum balance)
  • A way to write to the blockchain (e.g., send ether)
  • A way to read from a smart contract (e.g., ENS domain owner, or ERC20 token balance)
  • A way to write to a smart contract (e.g., create subdomain, or transfer ERC20 tokens)

Additionally, for your decentralized application to interact with a smart contract you need:

  • An Application Binary Interface (ABI)
  • The deployed address of the smart contract

I’ll cover the above in turn and show you how we apply them to our ĐApp.

Web3.js and MetaMask

The most convenient way to develop ĐApps is with Firefox or Chrome with the MetaMask plugin. For instructions on how to get it installed, please revisit the previous article.

As of this writing, there are two relevant versions of web3.js to consider when developing a decentralized app: The 0.20 and the not-quite-released-but-already-widely-in-use 1.0.0. In this article we are using the latter version.

The check for the web3 instance happens in the initWeb3() function. This will also work with upcoming MetaMask web3 injection changes:

window.addEventListener('load', () => {
    // If web3 is not injected
    if (typeof web3 === 'undefined') {
        // Listen for provider injection
        window.addEventListener('message', ({ data }) => {
            if (data && data.type && data.type === 'ETHEREUM_PROVIDER_SUCCESS') {
                // Use injected provider
                web3 = new Web3(ethereum);
                // ...
            } else {
                // No web3 instance available. Show a popup
                // ... 
        // Request provider
        window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, '*');
    // If web3 is injected, use its provider
    else {
        web3 = new Web3(web3.currentProvider);
        // ...

If no web3 instance is found, we can display a notification to the user:

Interacting with the Blockchain

Firstly, let’s get the current loaded Ethereum account, as done in the loadAccount() function:

web3.eth.getAccounts(function(error, accounts) {
    if (error) {
        // handle error
    } else {
        DApp.currentAccount = accounts[0];
        // ...

We can then auto-populate the input fields for the new owner and target with the address of the current account.

In our application, we do not need to interact with ether balances, but for completeness, here is how to check an account balance:

web3.eth.getBalance(DApp.currentAccount, function(error, ethBalance) {
    if (error) {
        // handle error
    } else {
        console.log("Eth balance", ethBalance);

…and here is how to send some ether:

        from: DApp.currentAccount,
        to: receiverAddress,
        value: ethValue
    function(error, txHash) {
        if (error) {
            // handle error
        } else {
            console.log("Transaction hash", txHash);

Note that you have to specify the from and to fields as Ethereum addresses and also the value of ether you want to send. This is represented in wei—e.g., 1000000000000000000 is one ether.

Interacting with Smart Contracts

In order to interact with a smart contract, we first have to create a proxy object. To do this, we need its application binary interface (ABI), which specifies the available operations.

We also need the Ethereum address where the smart contract is deployed on the blockchain.

In the application, we are interacting with one smart contract, which is our EnsSubdomainFactory:

factoryAbi: [...],

// Local
factoryAddress: "0x9fbda871d559710256a2502a2517b794b482db40",

The contract is initiated in the initContracts() function:

DApp.factoryContract = new web3.eth.Contract(DApp.factoryAbi, DApp.factoryAddress);

After this setup, we are finally ready to make some calls. For our use case, we need to know who the current owner of a particular domain and subdomain is, so this is how our checkSubdomainOwner() function looks:

DApp.factoryContract.methods.subdomainOwner(subdomain, domain).call()

Note that this is a read-only operation: We are using call() function, which is free to execute—i.e., it does not consume any gas.

This is how our ĐApp will show that a given domain is available, taken, or owned by the current user:

The final blockchain call we need to make is to create or update the subdomain. We do it inside of the newSubdomain() function:

    subdomain, domain, owner, target).send(
        gas: 150000,
        from: DApp.currentAccount
    function(error, txHash) {
        if (error) {
            // handle error
        } else {
            console.log("Transaction hash", txHash);

This time, we are passing four parameters to the function: subdomain, domain, owner, and target. They are self explanatory.

Note that we are using send() instead of call(). That’s because this is a write operation and will change the state of the blockchain, and will also cost you some ether as gas. An additional parameter that we include is gas which represents the gas limit for the transaction. If the transaction costs more than the limit to execute we want it to get reverted.

Listening for Events

We would like to know when our transaction completes successfully and notify the user that their subdomain is ready to use.

The factory smart contract defines an event which gets emitted when the subdomain is created or updated:

event SubdomainCreated(address indexed creator, address indexed owner, string domain, string subdomain);

Following the web3.js documentation, we should be able to do this:

        filter: {creator: DApp.currentAccount}
    function(error, event){
        if (error) {
            // handle error
        } else {
            // display a notification that the event happened

Where we specify the event we want to listen to SubdomainCreated and pass the filter parameter to only listen to events where the creator is specified as our DApp.currentAccount. Unfortunately, the current version of MetaMask does not yet support web sockets, which are required by web3.js 1.0 events. This works in older version of web3.js but with a different syntax.

As a workaround, we could poll for the transaction receipt to check if it’s been mined, but for simplicity, we will just show a link to Etherscan’s ENS lookup service with the details of our new subdomain, e.g., and the transaction status can be also found in the MetaMask window.

And this is all we really need to complete our simple ĐApp. For the full source code, feel free to dig in here.

Running Locally

If you want to play with the code locally, simply check it out and make sure you have the following npm packages installed:

npm i -g truffle
npm i web3 lite-server eth-ens-namehash-ms

You can launch the local testnet with Truffle:

$ truffle develop
truffle(develop)> migrate
truffle(develop)> test
Using network 'develop'.

  Contract: EnsSubdomainFactory
    ✓ creating new subdomain works (143ms)
    ✓ creating new subdomain fails when factory is not the owner of domain (47ms)
    ✓ updating subdomain works if done by current owner (215ms)
    ✓ creating new subdomain fails if it is already owned by someone else (197ms)
    ✓ transferring domain works (76ms)
    ✓ cannot transfer domains when locked (97ms)
    ✓ cannot transfer domains when not contract owner (55ms)
    ✓ checking for domain and subdomain owner works (138ms)

  8 passing (2s)

Finally, repoint the factory smart contract to your local node by uncommenting a line in dapp.js:

// Local
factoryAddress: "0x9fbda871d559710256a2502a2517b794b482db40",

// Ropsten
// factoryAddress: "0xf9fa2ff44a474b6d20500969bda61c2827fbc6b6",

// Mainnet
// factoryAddress: "0xbd185de5172ca64eec3d8cc763883a68f9154cd6",

To start the web server, simply execute npm run dev, which should launch your browser on localhost:3000.

Note that I’ve included ENS registry and resolver mock contracts for easy local development.

The Results on the Ethereum Network

The contract is deployed to 0xbd185de5172ca64eec3d8cc763883a68f9154cd6.

Our production ĐApp website is available at

The source code with MIT license is available on GitHub.

Disclaimer: This is running on the main network, not testnet, so you are operating with real money. Use at your own risk.

It’s important to note that domain owners can always change the subdomain owners—even if they are already assigned. We disabled this option in our smart contract, but you should be careful when you are using somebody else’s domain.

Welcome to the World of ĐApps

In this ĐApp tutorial, I’ve touched on the Ethereum Name Service (ENS), web3.js, ABI, smart contracts, MetaMask, transaction event listeners, and how to generate a Truffle project. This ĐApp does something interesting and useful, letting people create free Ethereum subdomains with a single blockchain transaction. Combined with my smart contract tutorial, I’ve given you enough detail (and hopefully the inspiration) for you to create your own Ethereum-based blockchain app. Best of luck!

Understanding the basics

  • What is a smart contract in a blockchain?

    A smart contract is a computer program that gets executed on the blockchain's nodes. Smart contracts can perform any calculation, persist data, define business rules, and also send and accept native currency like ether. Contracts are immutable in nature, unless programmed otherwise.

  • What do you mean by Domain Name System?

    The internet's Domain Name System (DNS) is a hierarchical, decentralized service translating human-friendly domain names like `` into numerical IP addresses like ``, which are required by computers on the Internet to locate data and serve content like websites.

  • What is an ENS domain?

    An Ethereum Name Service (ENS) domain is equivalent to a DNS domain and offers a decentralized and secure way to translate human-memorable text into Ethereum addresses. ENS domains currently end with .eth and have seven or more characters. They can be bought in an auction and can have hierarchical subdomains.

  • What is a decentralized application (i.e., dapp or ĐApp)?

    A ĐApp is simply a front-end website interacting with the blockchain back-end (usually via web3) instead of a traditional back-end with a server and a database. The website must be viewed on an Ethereum-enabled browser like Mist, Parity, or the mobile app Cipher; or by installing the MetaMask plugin.

  • Is a blockchain decentralized or distributed?

    All blockchains are distributed in nature as they have separate nodes working together in a distributed manner. Not all blockchains are decentralized, though. Some are centralized if there is a company that can censor, change, or stop the operation of the blockchain.

  • What is a subdomain and what is it used for?

    Subdomains are part of second-level domains, but are considered separate entities by search engines. They can be used to create different websites for different regions, languages, departments, user types, or device types. E.g., a dedicated mobile version of might use the subdomain.

Freelancer? Find your next job.
Remote Freelance Jobs
Radek Ostrowski

Located in Brisbane City, Queensland, Australia

Member since September 24, 2014

About the author

Radek is a blockchain engineer with an interest in Ethereum smart contracts. He also has extensive experience in machine learning.

Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

Join the Toptal® community.