Originally published at www.toptal.com, written by Radek Ostrowski.
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.
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, toptal.com
represents 35.186.228.167
—which would you rather remember and type?
$ ping toptal.com PING toptal.com (35.186.228.167): 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 DomainsENS 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. (I also just discovered ENSListing, which seems a bit more user-friendly.)
What about subdomains? Are they any simpler? Let’s have a look.
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:
[email protected] => 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:
radek.tenz-id.eth
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.
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.
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:
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.
To build any generic ĐApp, what you need is:
Additionally, for your decentralized application to interact with a smart contract you need:
I’ll cover the above in turn and show you how we apply them to our ĐApp.
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:
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:
web3.eth.sendTransaction( { 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.
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:
DApp.factoryContract.methods.newSubdomain( 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.
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:
DApp.factoryContract.once('SubdomainCreated', { 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., https://etherscan.io/enslookup?q=radek.freedomain.eth
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.
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 contract is deployed to 0xbd185de5172ca64eec3d8cc763883a68f9154cd6.
Our production ĐApp website is available at https://ens.startonchain.com.
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.
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!
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.
The internet’s Domain Name System (DNS) is a hierarchical, decentralized service translating human-friendly domain names like `toptal.com` into numerical IP addresses like `35.186.228.167`, which are required by computers on the Internet to locate data and serve content like websites.
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.
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.
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.
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 example.com might use the m.example.com subdomain.
Originally published at www.toptal.com.