
Published by Alex Van de Sande on July 12, 2016
Ethereum is not envisioned to serve as a platform for constructing obscure smart contract applications that necessitate a STEM degree to comprehend; rather, it aspires to be a foundational element of a different framework for applications on the internet. This article aims to clarify how this can be achieved and provide some fundamental examples of how to initiate the creation of a decentralized application.
Who is the target audience?
This article targets those with a basic grasp of web technologies and the ability to develop a simple JavaScript and HTML application, and desire to adapt these skills for building applications in the Ethereum ecosystem.
How can applications operate without servers?
Currently, servers in web applications perform far more tasks than they were initially designed for. In addition to serving static pages, they also manage private data, oversee user authentication, and handle all the complex methods in which data is processed and stored. The only role of the user’s device — once considered a supercomputer at the inception of the web — is to load and present that data to the user.
In contrast, a more decentralized structure would permit a significantly more modular approach, with various machines and protocols managing specific functions, some on the user’s end and others on specialized machines distributed throughout a peer-to-peer network. Thus, all Data logic (which data is stored, who stores it, conflict resolution, etc.) is managed by smart contracts on the blockchain, static files are delivered via Swarm and real-time interaction is facilitated through Whisper. The user’s device retains user authentication and operates the application’s interface.
Implementing this model would diminish the risk of data breaches and attacks since fewer single nodes hold large amounts of unencrypted data, while also alleviating the burden and expenses of serving applications by spreading it across the network. Given that all these protocols are decentralized, anyone can access the network and begin offering a specialized service: if the user is browsingfrom a robust laptop, for example, they can likewise provide static files to neighboring networks.
A decentralized framework also fosters creativity: as the interface is separate from the data, anyone is free to develop a new interface for the same application, thus cultivating a more dynamic and competitive environment. It could be said that one of the most fascinating and inventive phases in Twitter’s timeline occurred when it operated primarily as a central data source and allowed anyone to construct their own Twitter Application.
See it in action
If you’re eager to test the app prior to fully understanding it, we suggest you download Mist and consult our beginner’s guide on how to set up and operate the application. Alternatively, if you’re simply interested in viewing the entire application, it can be obtained directly from the Stake Voice Github repository.
Let’s dive in
We are set to create a straightforward application known as “Stake Voice”. The concept is to enable ether stakers to express their votes on any subject they choose, with the application tallying the total ether balance of all individuals who concur or dissent with the proposition.
The foundational contract of the application is crafted in Solidity, a javascript-like syntax which is quite elementary:
contract EtherVote { event LogVote(bytes32 indexed proposalHash, bool pro, address addr); function vote(bytes32 proposalHash, bool pro) { if (msg.value > 0) throw; LogVote(proposalHash, pro, msg.sender); } function () { throw; } }
The initial line establishes the contract name and the subsequent line generates an event titled “LogVote”, which will record the following in the logs:
- a hash of the proposal under voting
- whether the voter is in favor or against it
- the voter’s address
The “vote” function will subsequently trigger the log, which the application will later tally. Additionally, it includes a validation to ensure that no ether is sent unintentionally. The “anonymous” function executes when any ether is added to the smart contract and will effectively reject it.
To deepen your understanding of coding in Solidity, we recommend starting with the ethereum solidity tutorials, reviewing the official documentation page, and experimenting on your browser using the online compiler.
That’s fundamentally it: you select a hash,“`html
choose a position and perform Vote(). So how does this convert into a voting application?
Serverless Infrastructure
Adhering to the KISS principle, we are creating the simplest viable product that remains functional, which signifies that we will not utilize databases for holding proposals or implement any capabilities that depend on anything beyond basic JavaScript and standard HTML.
Thus, we will make use of the app’s URL to retain the proposal content, which we will then leverage to present to the user and create a hash that can subsequently verify the votes. Users can share their desired proposals for discussion via social media or opt for direct links.
// During the initial startup operation: proposal = decodeURI(getParameterByName('proposal')); //
Begin with the fundamentals
So obtain your preferred HTML framework and set up a basic site on your local device, then access it through Mist. All pages within Mist have access to a JavaScript object known as web3, where you will predominantly be operating. First, we should ascertain whether web3 is available:
Function init() { ... if(typeof web3 == 'undefined') { // Notify the user that they are not in a web3 compatible browser return; }
Some developers may wish to load their own web3 object to ensure future compatibility. To accomplish this, simply add just before
tag:
And then incorporate this into your initial function to load your custom web3 provider:
// Verifies Web3 support if(typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') { // If a web3 library is loaded, proceed to construct your own web3 web3 = new Web3(web3.currentProvider); } else if (typeof Web3 !== 'undefined') { // If there isn't then set ``````html a supplier web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); } otherwise if(typeof web3 == 'undefined') { // Notify the user that they are not within a web3 compatible browser return; }
Retrieve data from the blockchain
You verified you are connected to a blockchain, but which one is it? Is it the main ethereum network? Possibly a testnet or a private network? It might even be a future fork and your chain is entirely new. The most effective method to confirm this is to check whether the contract address you aim to access has any code present.
Moreover, to execute a contract, you need to know two fundamental elements: its address and the ABI, which will be a JSON encoded file that holds interface details.
var contractAddress = '0x1e9d5e4ed8ef31cfece10b4c92c9057f991f36bc'; var contractABI = [{"constant":false,"inputs":[{"name":"proposalHash","type":"bytes32"},{"name":"pro","type":"bool"}],"name":"vote","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"proposalHash","type":"bytes32"},{"indexed":false,"name":"pro","type":"bool" ``````html },{"indexed":false,"name":"addr","type":"address"}],"name":"LogVote","type":"event"}];
With that information in hand, it’s possible for you to verify the existence of the contract within the startup function:
// Retrieve the contract web3.eth.getCode(contractAddress, function(e, r) { if (!e && r.length > 3) loadContract(); })
It is also feasible to execute this command repeatedly in an attempt to reconnect using a different address (in case you are indeed connected to the testnet). Upon locating your contract, you can load it here:
Function loadContract() { // import the contract to javascript ethervoteContract = web3.eth.contract(contractABI); ethervote = ethervoteContract.at(contractAddress); }
Utilizing the web3 object permits you to establish a brand new javascript object capable of carrying out all ethereum operations directly via the browser. Should you wish to load merely a single instance of the contract, it’s possible to accomplish it in a single line:
ethervote = web3.eth.contract(contractABI ```).at(contractAddress);
Identify the user
Understanding the user’s account discloses a substantial amount of details about them: the balance of ether and any other tokens they possess, along with their transaction history. Therefore, having all applications recognize this by default could lead to a super cookie phenomenon, which would represent an intolerable breach of privacy. Conversely, mandating that the user register an account complete with login credentials for each website is not only bothersome for the individual, but also places their sensitive information under the control of third-party entities, resulting in significant honey pots that hackers can infiltrate.
Consequently, most individuals find that their personal and authentication data is managed by a handful of billion-dollar corporations. Privacy should not be a trade-off we accept for the sake of convenience: users ought to effortlessly authenticate into any application while retaining authority over their personal information.
With Mist, applications do not possess any details about the user until the individual chooses to disclose themselves to the application. When you wish to inquire about the accounts, the getAccounts function should be called:
web3.eth.getAccounts(function(e,accounts){ if (!e) { // do something with the accounts } });
At present, the returned object is an array that contains straightforward accounts accessible to the user, but in the future, it will also incorporate smart contract accounts the user employs for identification purposes. This will enable users to access functionalities currently reserved for centralized authenticators, such as two-factor authentication or cloud backup, along with future advancements unique to smart contracts, like permitting a few trusted friends to grant you access to an account for which you’ve lost the keys or facilitating automatic inheritance of dormant accounts.
Every forthcoming Ethereum browser will manage how users identify themselves to the application. Within Mist, we have two methods: either the user can initiate it by pressing the “connect” button (currently labeled as a “no accounts” button) or the application can solicit authentication by invoking the “requestAccount” API.
Notice: the accounts listed here are merely those that the user claims to control the key for, but the user has not provided any evidence of this; therefore, you may present a different interface, but do not transmit any confidential information designated solely for that account. If you require verification of the user’s identity, you will need them to sign a message. While Mist will also support this in the future, be aware that it would necessitate an additional step for the user to enter their password, so this should only be implemented when absolutely essential.
Voting
Once you possess the contract as an object, executing votes is simply a matter of invoking it from JavaScript. This will trigger a Mist transaction pane, allowing the user to verify the transaction before entering their password. Thus, we will first create two interactive objects that call a vote function:
document.getElementById('vote-support').addEventListener('click', function(){ vote(true);```html }, false); document.getElementById('vote-against').addEventListener('click', function(){ vote(false);}, false);
Observe that one invokes the function with a true argument while the other employs false. The vote function may be as straightforward as:
Function vote() { ethervote.vote(proposalHash, support, {from: web3.eth.accounts[0]}); }
“Ethervote” represents the entity we established earlier, and “vote” is one of its methods, corresponding to one of the contract methods:
function vote(bytes32 proposalHash, bool pro) {}
We supply the two required parameters for the function and then incorporate a third object that includes transaction details, such as the sender’s address and, optionally, how much gas to allocate or the cost of the gas.
As a result, this would prompt a dialog requesting the user to verify the transaction – yet it will likely produce an error because the web3.eth.accounts object is an empty array by default; therefore, it is necessary to validate this and if it is empty, prompt the user to provide the accounts:
function vote(support) { web3.eth.getAccounts(function(e,accounts){ // Verify if there are accounts accessible if (! ``````html e && accounts && accounts.length > 0) { // Create a dialog initiating the transaction ethervote.vote(proposalHash, support, {from: accounts[0]}) } else { mist.requestAccount(function(e, account) { if(!e) { // Create a dialog initiating the transaction ethervote.vote(proposalHash, support, {from: account.toLowerCase()}) } }); } }); }
You ought to only ask for an account when the user has initiated an action: unexpectedly pinging a transaction will rightfully frustrate the user and likely prompt him to exit your application. Should we notice misuse of this feature by applications, we may implement more stringent requirements regarding when a notification appears.
Monitor the contract
Lastly, in order to tally all the votes, it’s necessary to observe the contract events and determine which votes were cast. To accomplish this, we must execute this function once to commence monitoring the events, subsequent to instantiating “ethervote”:
ethervote = web3.eth.contract(contractABI).at(contractAddress); var logVotes = ethervote.LogVote({proposalHash: proposalHash}, {fromBlock: 1800000}); // Wait for the events to load logVotes.watch(function(error, result){ if (!error) { // Perform an action whenever the event ```happens receivedEvent(result); } })
The code provided above will commence retrieving all blocks from 1.8M onwards (the moment the contract was deployed) and subsequently execute the receivedEvent() function for each event. Each time a new block is received containing an event, this function will be activated again, thus eliminating the need for constant calls. So, what exactly does this function do?
Var voteMap = {}; Function receivedEvent(event) { // Retrieve the present balance of a voter var bal = Number(web3.fromWei(web3.eth.getBalance(event.args.addr), "finney")); voteMap[res.args.addr] = {balance: bal, support: event.args.pro}; }
From the original solidity contract, you can observe that the LogVote event includes three arguments: proposalHash, Pro, and Addr:
event LogVote(bytes32 indexed proposalHash, bool pro, address addr);
Thus, the operation of this function entails utilizing the web3.eth.getBalance function to verify the current ether balance of the voting address. All balances consistently return figures in wei, which represents 1/1000000000000000000 of an ether and is not particularly useful in this context; therefore, we utilize a different web3 function that converts it into any ether unit of our choice. In this instance, we will use finney, which corresponds to a thousandth of an ether.
Following that, the function will store the balance along with the voter’s position in a mapping based on their address. One benefit of employing a mapping in place of an array is that it will automatically override any previous data relating to the same address; thus, if an individual votes multiple times, only their most recent stance will be retained.
Additionally, we could potentially identify the user and indicate whether they have voted or not.
// Verify if the present owner has already cast their vote and display that on the interface web3.eth.getAccounts(function(e,accounts){ if (```html !e && accounts && accounts[0] == res.args.addr) { if (res.args.pro) { // User has cast a positive vote! } else { // User has voted negatively! } } });
Count the votes
In conclusion, we need to introduce a distinct function for totaling the votes:
What is the reason for tallying votes in a separate function? Since the weight of each vote is derived from the current balance of every account, it is essential to recalculate these balances with every new block, even if no new event has transpired. To accomplish this, you can implement the following function that triggers automatically with each new block entry:
web3.eth.filter('latest').watch(function(e, result){ if(!e) { calculateVotes(); } });
Eventually, we will move on to the final total calculation. Previously, we utilized eth.getBalance in synchronous fashion, where the application awaited the outcome of the preceding action. Now, since multiple actions may be invoked during each block, we will employ it in asynchronous mode: calling the node and executing the action when it responds, all while keeping the interface fluid.
var totalPro, totalAgainst, totalVotes; function calculateVotes() { totalPro = 0; totalAgainst = 0; totalVotes = ``````html 0; Object.keys(voteMap).map(function(a) { // invoke the function asynchronously web3.eth.getBalance(a, function(e,r) { voteMap[a].balance = Number(web3.fromWei(r, 'finney')); if (voteMap[a].support) totalPro += parseFloat(voteMap[a].balance); else totalAgainst += parseFloat(voteMap[a].balance); // perform something exciting with the outcomes! }); }); }
As you can see from the code, the application iterates through each voting address to fetch their balance, and upon receiving it, it will either contribute to the pro or against camp and calculate the totals.
A few additional warnings: if no events occur, nothing will be retrieved and votes won’t be computed, so it’s advisable to include a timeout mechanism in all functions that depend on events from the blockchain.
setTimeout(function(){ // In case the application does not respond post a timeout it likely has no votes }, 3000);
Now, you are free to employ all your current web development techniques to create whatever impressive outcome you desire. Use the figures to craft a captivating 3D visualization or link to your preferred social media to promote the most intriguing questions.
Mist also aims to streamline your code by offering some fundamental navigation and UI functionalities. If you wish for your application to be headerless and take up the full height of the mist app, simply add the following to your
tag:
```meta name="ethereum-dapp-url-bar-style" content="transparent">
In the event that you wish to utilize Mist itself for navigating your application, you can employ the Mist.menu object:
for (item of propHistory) { if (item.length > 0 && item != 'null') { mist.menu.add( item ,{ name: item, position: n++, selected: item == proposal }, function(){ window.location.search = '?proposal=' + encodeURI(this.name); }); } }
An excellent feature of Ethereum is the capability to build upon this straightforward contract functionality without requiring authorization: you can append additional functions on distinct contracts, maintaining each one simple and more manageable for troubleshooting. Furthermore, it allows others to incorporate the contracts you established into their applications, thereby providing new functionality. Meanwhile, all applications access the same data and backend.
You can experiment with this application live hosted on github pages, but keep in mind that this isn’t the authoritative source, just one of numerous potential interfaces to it. The same application will also function as a local HTML file on your device or on an IPFS network and it is expected that it will be downloaded directly via Mist utilizing Swarm in the future.
Here are some concepts on how you might explore:
- Form a list of currently accessible statements. Anyone can verify them by viewing the sha3 of the proposal text, hence you don’t require permission.
- Establish threaded comments where users can respond to statements and subsequently upvote or downvote them, akin to a decentralized stake-based Reddit
- Rather than (or in addition to) utilizing ether balance, you could use another Ethereum token, such as The DAO or Digix Gold to differentiate your questions. Given that all the original contract registers is the sender, you can review all balances. Alternatively, you might create your own currency based on reputation, karma, or another criterion.

