Tutorial: How to build a completely free Dapp

A JavaScript Voting Dapp, based on IPFS, IOTA, and the Web Crypto API

Since the exact definition of a decentralized application (Dapp) is still debatable, let’s simply assume in this article that a Dapp is an application run on a decentralized network without any centralized part like a regular backend. Dapps have numerous advantages (e.g., security by design), but currently, most of the time they have three mandatory requirements, that make them uninteresting for most people:

Installation of additional software Complicated sign-up process Necessary transaction fees

On top of it, the typical Dapps are quite expansive to run.

In this tutorial, we show how to create a simple voting application called DVote, which can be hosted completely for free on IPFS and doesn’t require any sign-up process or installation. We achieve this by running almost everything in the browser and only using the distributed ledger technology where it makes sense. DVote uses IOTA, the Web Crypto API, and IPFS. The idea is based on the following popular Ethereum Dapp Tutorial. The purpose of this application is to give you an idea of what is possible with this technology, so it’s not a finished product. For example, to make this really useful you would need to show the load progress (currently you just have to wait) as well as a simple login system, which prevents people from voting multiple times. You can find the full open source code on GitHub.

The picture shows the finished application

Get started

First, we need to set up our development environment. Make sure you have Node.js and NPM installed. Then run the following commands and answer the questions.

npm init

npm i -D webpack webpack-cli mini-css-extract-plugin css-loader html-webpack-plugin html-webpack-inline-source-plugin

Now you can add the following webpack.config.js to your project:

Webpack helps us to create one single HTML file, which contains the complete source code of our application. This way the app can very easily be upload and distribute on IPFS. To start not from scratch, we use the open source project Versus and put everything unzipped inside the src folder. However, we need to change this project a little bit for our purpose.

2. HTML

The HTML file is our default template for the application. Therefore, we combine both HTML files into one file by copying the whole <div class=”survey-wrapper”> of the output.html into the index.html. We use JavaScript below to display only one part of the application based on the URL.

Additionally, we need to remove all the JavaScript and CSS-imports from the HTML document as well as add some new identifiers. It’s important also to change the old identifiers of the output.html, so you don’t have the same identifier twice in one HTML document. Feel free to also edit the default text or document layout at this stage. For example, we decided to change the previous host input field to show the random IOTA address which we will later use. Therefore, we also make the input field read-only (readonly=”readonly”). Finally, we rename the index.html to dvote.html, just to improve the searchability of this file on the distributed web.

You can find our final HTML file here.

3. CSS

Since Webpack now takes care of our CSS import, we need to add the CSS import to the app.js like below and remove it from every CSS file:

import ‘../css/input.css’;

import ‘../css/main.css’;

import ‘../css/output.css’;

import ‘../css/style.css’;

We also remove the background parts inside the form-wrapper of the input.css and add the following line to the body inside the main.css.

background: linear-gradient(135deg, #2a99ef 20%, #52d9e5 80%);

This way we don’t have to upload an additional background image on IPFS, which should improve the loading time of the application. If you want, you can now remove the image folder since we don’t need it anymore. We also make the previous host input field grey, since its now read-only (see above) and make additional small changes. For example, we change the text alignment of the survey title and info to center (text-align: center;), but overall feel free to get creative here.

The final CSS files can be found here.

4. JavaScript

Now we take a closer look at the JavaScript. The final files can be found here.

4.1 Layout

First, we redesign the app.js file so that it reads the URL Parameters. Depending on these parameters, it will show the layout for creating a survey or voting. To achieve this we copy the getURLParameter.js from Dweb.page to our project. And since you’re here anyway, feel free to also copy the replaceAll.js. Once the window is loaded, we start the initLayout function:

async function initLayout() {

const urltitle = getURLParameter(‘title’);

…

if (typeof urltitle !== ‘undefined’){

voteLayout(urliotaAdd, urltitle, urloptA, urloptB);

} else {

createSurvey();

}}

This layout function then displays the appropriate elements (e.g., document.getElementById(‘divVoteLayout’).style.display = ‘none’;) as well as adds the correct logic.

4.2 Immutable backend

Next, we add an IOTA subfolder to the js-folder. The main reason why we use IOTA for the project is that transactions are feeless. Here we add the getHealthyNode, Iota and iotaConfig files of Dweb.page. For simplicity, we remove everything we don’t need for this project and integrate the external dependencies of these files. Furthermore, we change the getTransactionByAddress and vote function. A new function we add is the generateSurveyAddress function. This function makes sure that every new survey will get its own random and unique IOTA address.

Additionally, we add the IOTA and trytes library to our project, with

npm i iota.lib.js trytes

In the app.js we create a new instance of the Iota class:

const iota = new Iota();

We establish the connection to a random healthy node:

await iota.nodeInitialization();

As well as send the votes to the Tangle:

iota.vote(urliotaAdd, voteObj);

And load existing votes from the Tangle:

const transactions = await iota.getTransactionByAddress(urliotaAdd);

for (let i = 0; i < transactions.length; i += 1) {

awaitMessages.push(iota.getMessage(transactions[i]));

}

For information about IOTA feel free to take a look at the official documentation.

4.3 Signature system

For this part, we use the crypto folder of Dweb.page and integrate a signature system into DVote. Since we don’t integrate an additional login system here, it’s not super useful on its own, but it shows you how you can use the Web Crypto API for your own projects. You can take a closer look at the Dweb.page project to see how you can use it to encrypt information or store non-extractable private keys inside IndexedDB. Furthermore, the Web Crypto examples might be useful for your future projects.

For the Dweb.page compression of the signature key we need to add the big-integer NPM package:

npm i big-integer

In the app.js for voting we generate the keys and extract the public key with the following lines of code:

const sig = new Signature();

const keys = await sig.getKeys();

const publicHexKey = await sig.exportPublicKey(keys.publicKey);

And then create a valid signature with:

const signature = await sig.sign(keys.privateKey, JSON.stringify(voteObj));

Finally, we verify the signature of existing votes with the following line:

const isVerified = await sig.verify(await sig.importPublicKey(messages[j].publicKey),

5 Hosting

Let’s create the finished dvote.html inside the dist/html folder by running the following command:

npm run build

If you open Dweb.page, select the public sharing mode and drag and drop your dvote.html on it. That’s it! Your distributed voting application is hosted on the distributed web and can be accessed via any IPFS gateway and by adding the correct hash.

You can check out our version here and feel free to answer our test survey.

Image of the test survey

Let us know if you are interested in us providing a JavaScript library for developers based on this tutorial. Or help us to improve the underlying technology as well as the tutorial itself: