I was doomscrolling on YouTube sometime around December last year, when I came across a video by Cleo Abram where she explained why the United States did not make use of E-Voting systems to carry out the 2024 General Elections. While I can’t explain everything she said in the video, it got me thinking: “Can All These Issues Be Solved via Decentralization? ”. This motivated the application I built for my final year project.
For my final year project, I built a blockchain-based voting system that uses smart contracts to make elections transparent, tamper-resistant, and verifiable. The idea was simple: every vote is recorded on the blockchain, and no one can change the results once they’re in.
In this guide, I’ll walk you through what building such a system entails from a developer’s perspective — the architecture, the smart contract design, how the frontend interacts with the blockchain, and some of the challenges I faced along the way. I will not be speaking in any programming language specific terms, so as to give everyone an understanding of the system.
Problem Statement & Goals
The system was aimed at addressing two main issues. Simplicity for non-technical voters, and Security/Transparency of cast votes. This meant that the voting interface was made as easy as possible to understand, while votes were stored in the blockchain for security and displayed to users in real time for transparency.
High-Level Architecture
The system was designed with a semi-decentralized architecture in mind. Instead of a completely decentralized system, the registration of voters is done by an electoral commission, and login credentials given to the voters to login.
At its core, the voting system I built has three main parts:
The smart contract: this lives on the blockchain and is responsible for storing candidates, recording votes, and making sure no one can cheat (for example, by voting twice).
The frontend interface: this is what the voter sees: a simple page where they can connect their wallet, view candidates, and cast a vote.
The backend/indexer: this listens to blockchain events and stores them in a database so results can be shown quickly and cleanly, without querying the chain every time.
Here’s how they work together: the frontend lets a voter choose a candidate → their wallet signs the transaction → the smart contract records the vote on-chain → the results can be read either directly from the contract or through the backend, which makes things faster and easier to display.
I deliberately kept most of the logic on-chain so that security and transparency were guaranteed. The trade-off is that everything stored on the blockchain is public, but for the purposes of my project, that was acceptable since the main goal was to prove tamper-resistance and verifiability.
Smart Contract Design
The smart contract, written in Solidity, handled verification of authenticity of votes, vote storage and retrieval via some major functions. The first function was triggered when the backend sent the list of candidates to the contract for storage. This meant that only votes related to these candidates would be allowed. The second function handled vote validation and storage and was triggered when an event (Vote Casting) was triggered. This function retrieved the ID of the voter and the ID of the candidate that was voted for. This is then hashed to ensure it remains secure and is stored on the blockchain. The last function simply sends the number of votes cast for each candidate back to the backend for display on the frontend.
function vote(uint256 candidateId) public {
require(!hasVoted[msg.sender], "You have already voted.");
require(candidates[candidateId].id == candidateId, "Invalid candidate.");
candidates[candidateId].voteCount += 1;
hasVoted[msg.sender] = true;
emit VoteCasted(msg.sender, candidateId);
}
Furthermore, some constraints were implemented on the contract. Only one vote can be cast per address (User). Casting more than one vote will trigger an error and reject the vote.
Development Workflow
Some of the tools used for development included Remix IDE for writing and deploying Solidity smart contracts to the Ganache Ethereum testnet which was used to simulate the blockchain network.
The frontend was developed using HTML, CSS and JavaScript and retrieved data from the backend via API requests. The backend was developed using the Django framework which allowed for ease of scale in the long term and communicated with the smart contract via web3.py. So it was essentially a middleman between the frontend and the smart contract.
Challenges Faced and Lessons Learned
Building this system was a roller-coaster especially because I had four days to do so and in the course of this, many challenges were faced and many lessons learned. Some of these challenges included the complexity of working with a new language as I hadn’t worked with Solidity prior to this. Although not a major issue, scalability of the system was another issue faced as the speed of the system reduced as more voters were added to the system but given the fact that this was just a prototype, it was negligible.
Gas fees and cost of transactions was not an issue given the fact that this was done on the Ganache testnet, but in a live system, gas optimization will definitely be something to look into.
What do you think? Is a semi-decentralized model the right approach for secure digital voting? Have you built something similar? I'd love to hear your thoughts in the comments!
Top comments (0)