Building decentralized applications (DApps) on the Ethereum blockchain opens the door to trustless, transparent, and secure digital interactions. In this guide, we’ll walk through creating a simple voting DApp — a practical example that demonstrates core concepts of Ethereum development, from writing smart contracts in Solidity to deploying and interacting with them via a web interface.
Whether you're new to blockchain or expanding your Web3 development skills, this step-by-step tutorial covers everything you need to build, deploy, and interact with a functional DApp using modern Ethereum tools.
Setting Up the Development Environment
Before diving into coding, ensure your local machine has the necessary tools installed:
- Node.js and npm: The runtime and package manager for JavaScript.
- Git: For version control and dependency management.
- Solidity compiler (solc): To compile your smart contract code.
- Web3.js: A JavaScript library for interacting with Ethereum.
- Ganache (formerly testrpc): A personal Ethereum blockchain for testing.
Install these tools using your preferred package manager. For example:
npm install -g solc web3 ganacheOnce installed, you can launch a local blockchain for testing:
👉 Start building your first DApp with a secure development environment setup
Writing the Smart Contract in Solidity
We'll create a simple voting system where users can vote for candidates and view real-time vote counts. Below is the Voting.sol contract written in Solidity:
pragma solidity ^0.4.11;
contract Voting {
mapping (bytes32 => uint8) public votesReceived;
bytes32[] public candidateList;
function Voting(bytes32[] candidateNames) {
candidateList = candidateNames;
}
function totalVotesFor(bytes32 candidate) returns (uint8) {
if (!validCandidate(candidate)) revert();
return votesReceived[candidate];
}
function voteForCandidate(bytes32 candidate) {
if (!validCandidate(candidate)) revert();
votesReceived[candidate] += 1;
}
function validCandidate(bytes32 candidate) returns (bool) {
for(uint i = 0; i < candidateList.length; i++) {
if (candidateList[i] == candidate) {
return true;
}
}
return false;
}
}Key Features:
- Uses a
mappingto store votes by candidate name (asbytes32). - Accepts an array of candidate names during deployment.
- Includes validation to prevent voting for invalid candidates.
- Exposes functions to query votes and cast ballots.
Note: While newer versions of Solidity userequire()andrevert(), this example uses older syntax (throw) for compatibility with legacy tutorials.
Compiling the Contract
Open a Node.js console and run the following commands to compile the contract:
Web3 = require('web3');
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
solc = require('solc');
code = fs.readFileSync('Voting.sol').toString();
compiledCode = solc.compile(code);The compiledCode object contains both the ABI (Application Binary Interface) and bytecode needed for deployment.
Deploying the Smart Contract
Deployment connects your compiled contract to the Ethereum network — in this case, your local test chain.
Extract the ABI and bytecode:
abiDefinition = JSON.parse(compiledCode.contracts[':Voting'].interface);
byteCode = compiledCode.contracts[':Voting'].bytecode;Create a contract instance and deploy it:
VotingContract = web3.eth.contract(abiDefinition);
deployedContract = VotingContract.new(
['Rama', 'Nick', 'Jose'],
{ data: byteCode, from: web3.eth.accounts[0], gas: 4700000 }
);After deployment, retrieve the contract address:
deployedContract.address;
contractInstance = VotingContract.at(deployedContract.address);This contractInstance allows you to interact with the deployed contract programmatically.
Interacting via Console
Test the contract directly in the Node.js console:
// Check current votes
contractInstance.totalVotesFor.call('Rama');
// Cast votes
contractInstance.voteForCandidate('Rama', { from: web3.eth.accounts[0] });
// Verify updated count
contractInstance.totalVotesFor.call('Rama').toString();Each transaction simulates real-world behavior — changing state on the blockchain and consuming gas.
👉 Learn how blockchain transactions work under the hood
Building a Web Interface for User Interaction
Now let’s make the DApp accessible through a browser. Create two files: index.html and index.js.
index.html
<!DOCTYPE html>
<html>
<head>
<title>SSC VOTING APPLICATION</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<h1>SSC VOTING APPLICATION</h1>
<div id="candidates">
<table>
<tr><th>Candidate</th><th>Votes</th></tr>
<tr><td>Rama</td><td id="candidate-1"></td></tr>
<tr><td>Nick</td><td id="candidate-2"></td></tr>
<tr><td>Jose</td><td id="candidate-3"></td></tr>
</table>
</div>
<br/>
<input type="text" id="candidate" placeholder="Candidate Name" />
<button onclick="voteForCandidate()">Vote</button>
<script src="index.js"></script>
</body>
</html>index.js
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
abi = JSON.parse('[{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"validCandidate","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"x","type":"bytes32"}],"name":"bytes32ToString","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"candidateList","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"contractOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"candidateNames","type":"bytes32[]"}],"payable":false,"type":"constructor"}]');
VotingContract = web3.eth.contract(abi);
contractInstance = VotingContract.at('0x4131a0f92d36932d3ec3b7a0581546f2e662ad0b'); // Replace with your deployed address
candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"}
function voteForCandidate() {
let candidateName = $("#candidate").val();
contractInstance.voteForCandidate(candidateName, {from: web3.eth.accounts[0]}, function() {
let div_id = candidates[candidateName];
$("#" + div_id).html(contractInstance.totalVotesFor.call(candidateName).toString());
});
}
$(document).ready(function() {
Object.keys(candidates).forEach(function(name) {
let val = contractInstance.totalVotesFor.call(name).toString();
$("#" + candidates[name]).html(val);
});
});Replace the contract address with your own after deployment.
Open index.html in a browser (served over HTTP, not file://), connect MetaMask to localhost:8545, and start voting!
Frequently Asked Questions
Q: What is a DApp?
A: A decentralized application (DApp) runs on a blockchain network instead of a central server. It uses smart contracts for logic and provides censorship-resistant functionality.
Q: Why use Ganache instead of the mainnet?
A: Ganache offers a safe, fast, and free environment for testing without risking real funds or paying high gas fees.
Q: Can I use Truffle or Hardhat instead?
A: Yes! Tools like Truffle and Hardhat streamline compilation, testing, and deployment. This guide uses raw tools to clarify underlying processes.
Q: How do users interact with my DApp?
A: Users connect via wallets like MetaMask, which injects a Web3 provider into the browser, enabling direct interaction with smart contracts.
Q: Is Solidity still relevant?
A: Absolutely. Solidity remains the most widely used language for Ethereum smart contracts, supported by extensive tooling and community resources.
Q: Can I upgrade this voting DApp for production?
A: Yes — add features like voter registration, vote delegation, or integration with identity systems (e.g., ENS). Also consider security audits and gas optimization.
Core Keywords for SEO
- Ethereum DApp development
- Solidity smart contract
- Build decentralized app
- Voting DApp tutorial
- Web3.js interaction
- Smart contract deployment
- Blockchain voting system
- Ganache testnet
These keywords are naturally integrated throughout the content to enhance search visibility while maintaining readability.
With this foundation, you’re ready to explore more advanced topics like event logging, front-end frameworks (React/Vue), and integrating wallet connectivity. The world of Web3 awaits — start building today.