Skip to main content

Command Palette

Search for a command to run...

Building a Real-World Blockchain Voting System in Solidity

Learn how to build a secure, time-controlled voting system using Solidity.

Published
•4 min read•View as Markdown
Building a Real-World Blockchain Voting System in Solidity

A Clean, Secure, and Time-Limited Way to Run Elections On-Chain

“Just let people vote on the blockchain” — sounds easy, right?

Until you start asking the real questions:

  • What if voting never ends?

  • What if someone registers twice?

  • What if bots try to game the vote?

  • What if you need to know exactly when and how someone voted?

That’s where most "basic" smart contracts fail.
In this article, I’ll show you a professional-grade voting contract built in Solidity that solves these problems.


Why This Voting Contract is Different

This contract isn’t a toy. It’s built with real elections in mind:

  • Moderator-based candidate registration

  • Owner-controlled voting window (in hours)

  • Aadhaar-based candidate uniqueness (or any national ID)

  • One vote per person, no bots

  • Transparent logs through events

  • Winner can only be announced after voting ends

Let’s unpack how it works 👇


Candidate Registration: Trust First

struct Candidate { 
    uint64 aadharNo; 
    string name; 
    string symbol;
    uint256 voteCount;
    uint64 id;
 }
  • ✅ Aadhaar is your identity lock: Candidates register with a unique number (like a government ID).

  • ✅ Only moderators can register candidates — not the public, not even the owner.

  • ✅ Duplicate checks built-in: aadhaarUsed[_id] makes sure no one registers twice.

Why it matters:
Election integrity starts with clean candidate data. This avoids scams, duplicates, and fake submissions.


Time-Limited Voting (Just Like the Real World)

function startVoting(uint256 _durationHours) external onlyOwner {
        require(candIds.length > 0, "No candidates to vote for");
        require(votingStart == 0, "Voting already started");
        votingStart = block.timestamp;
        votingEnd = block.timestamp + (_durationHours * 1 hours);
        emit VotingStarted(votingStart, votingEnd);
    }
  • The owner sets the clock. You choose when voting starts and for how long (in hours).

  • Voting can't start twice. One election per deployment unless extended manually.

  • Everything is tracked by timestamps. Your frontend can show countdowns in real time.

Why it matters:
Without time control, elections are chaotic. This enforces deadlines and fairness.


One Person, One Vote — No Bots Allowed

 function doVote(uint64 id) external onlyDuringVoting {
        require(msg.sender == tx.origin, "Contracts not allowed to vote");
        require(!hasVoted[msg.sender], "Already voted");
        require(exists[id], "Invalid candidate");
        hasVoted[msg.sender] = true;
        candidates[id].voteCount += 1;
        totalVotes += 1;
        emit Voted(msg.sender, id);
    }
  • Voters must be human (not another contract)

  • Each address gets one shot

  • Candidate must be valid and registered

Why use tx.origin?
It’s a simple way to block smart contracts from calling doVote. This helps prevent bot voting.


Announcing the Winner (Only After the Clock Ends)

function getWinner() external view onlyOwner returns (string name, uint256 votes) { 
    require(block.timestamp > votingEnd, "Voting not over yet");
     // scan and return winner... 
     }
  • You can’t cheat the system by ending early.

  • No early access to results.

  • The logic loops through all candidates and returns the one with the most votes.

Why it matters:
No one can manipulate or read results prematurely. Everyone sees the outcome together.


Frontend-Ready with Clean Events

event VotingStarted(uint256 start, uint256 end);
event CandidateAdded(uint64 indexed id, string name);
event Voted(address indexed voter, uint64 candidateId);

Your dApp or backend can:

  • Start countdown when VotingStarted fires

  • Build the ballot when CandidateAdded emits

  • Track turnout with every Voted event


What This Contract Does Better Than Most

FeatureBasic VotingThis Contract
Time-controlled elections❌✅
EOA-only voting (no bots)❌✅
One vote per address❌✅
Duplicate Aadhaar protection❌✅
Role-based candidate registration❌✅
Emit events for frontend tracking❌✅

Real-World Use Cases

  • University elections

  • DAO governance (with voting windows)

  • Community contests or NFT drops

  • 🇮🇳 Local or regional testbed for Aadhaar-based elections

  • Educational demo for smart contract best practices


How to Use

  1. Clone the GitHub repo

  2. Deploy using Hardhat or Remix

  3. Call addModerator(...)

  4. Moderator calls addCandidate(...)

  5. Owner calls startVoting(2) → starts 2-hour election

  6. Users vote with doVote(candidateId)

  7. After end, owner calls getWinner()


What’s Next?

Here are some ways to improve this even more:

  • Add support for regions or constituencies

  • Multiple elections in one contract (Election struct)

  • Candidate withdrawal logic

  • Whitelisted voter registration

  • On-chain live dashboard for vote counts


Final Thoughts

This smart contract bridges the gap between academic tutorials and real-world voting systems.

It’s secure.
It’s fair.
And it’s flexible enough to power elections at any scale.

If you're building a DAO, a community project, or testing blockchain for governance — this contract gives you a strong foundation.

🧠 Feedback, improvements, or collaboration?
📬 Open a PR or reach out on Twitter @yourhandle

👉 View Code on GitHub

More from this blog

Code and Consciousness

10 posts

Writing code. Exploring consciousness.