- Combine a file-level struct, an enum, a mapping and a dynamic array in one contract.
- Protect actions with modifiers (admin-only and one-vote-per-address).
- Compute and publish an election result on-chain.
Synopsis
Online voting needs results that nobody can quietly alter. A blockchain is a good fit: every vote is a transaction recorded permanently, and anyone can check that the count follows the published rules.
In this project the deploying account becomes the BlockAdmin (election administrator). The admin cannot change votes; the admin’s role is to view the total number of votes cast and to calculate and publish the winner.
Every other account acts as a voter. A voter casts a vote by calling the function of the chosen candidate (or NOTA). The contract records the voter’s address and rejects a second vote from the same address, so no one can vote twice.
Contract Architecture
| Part | Details |
|---|---|
| CandidateMaster.sol | File-level struct Candidate { string candidateName; string candidateParty; uint voteCount; } |
| VotePanelContract.sol | Contract VotePanel; imports contracts/CandidateMaster.sol |
| enum | candidateCode { AJIT, ASHISH, MAYANK, KUNAL, NOTA } – readable keys 0–4 |
| mapping | mapping(uint => Candidate) candidateList – candidate code → candidate record |
| dynamic array | address[] votersList – addresses that have already voted |
| state | blockAdmin, totalCandidate (= uint(type(candidateCode).max) + 1 = 5), Candidate public winner |
| constructor | Sets blockAdmin = msg.sender and loads the five candidates (sample names) with 0 votes. |
| modifiers | checkBlockAdmin() – only the admin; checkVoterCasting() – loops over votersList and rejects an address that already voted. |
| voting functions | One function per candidate (e.g. Ajit_Singh__Red_Party(), NOTA()): records the voter and increases that candidate’s voteCount. |
| admin functions | voteCountAndResult() – finds the candidate with the most votes and stores it in winner; totalVotesCasted(); viewBlockAdmin(). |
How It Works
winner is publicThe result loop uses if (maxCount < candidate.voteCount), so in a tie the candidate with the lower code (declared earlier) remains the winner.
Key Requirements (Concepts Used)
- Struct – Candidate record (Chapter 13) declared at file level and imported (Chapter 2).
- Enum – candidate codes and
type(candidateCode).max(Chapter 11). - Mapping – code → Candidate (Chapter 12).
- Dynamic array – list of voters (Chapter 10).
- Functions, view functions and public getters (Chapter 15).
- Modifiers – admin and duplicate-vote checks (Chapter 22).
- Constructor and msg.sender (Chapters 14 and 17).
How to Run in Remix
- In Remix, create
contracts/CandidateMaster.soland paste the CandidateMaster.sol resource file. The import path in the main file expects exactly this name and folder. - Create
contracts/VotePanelContract.soland paste the VotePanelContract.sol resource file. - Compile with a 0.8.x compiler and deploy VotePanel from the first account – it becomes BlockAdmin.
- Switch to other accounts and click one candidate function per account. Try voting twice from one account to see the “already casted” error.
- Switch back to the admin account, call
voteCountAndResult(), then clickwinnerandtotalVotesCasted.
Limitations and Extension Exercises
| Observation in the teaching version | Improvement to try |
|---|---|
| The duplicate check loops over every voter, so gas grows with each vote. | Use mapping(address => bool) hasVoted for a constant-cost check. |
| One function per candidate; adding a candidate needs new code. | Write one vote(candidateCode c) function. |
| Any address can vote; there is no voter registration. | Let the admin register eligible voters (whitelist) before voting starts. |
| No voting period; the result can be computed at any time. | Add start/end times or an enum state (Registration → Voting → Closed). |
| Ties are resolved silently in favour of the earlier candidate. | Detect ties and report them. |
| Votes are public on-chain (no secret ballot). | Study commit–reveal voting for privacy. |
The function name Mayank_Jain__Green_Party does not match the stored party (“Blue Party”). | Keep function names and data consistent – or use the single vote() function. |
Exercise solution (one possible approach)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract VotePanelV2 {
enum Code { AJIT, ASHISH, MAYANK, KUNAL, NOTA }
address public immutable admin = msg.sender;
bool public votingOpen = true;
mapping(address => bool) public hasVoted; // O(1) duplicate check
mapping(Code => uint) public votes;
uint public totalVotes;
modifier onlyAdmin() { require(msg.sender == admin, "Not admin"); _; }
function vote(Code c) external {
require(votingOpen, "Voting closed");
require(!hasVoted[msg.sender], "Already voted");
hasVoted[msg.sender] = true;
votes[c] += 1;
totalVotes += 1;
}
function closeVoting() external onlyAdmin {
votingOpen = false;
}
function leader() external view returns (Code best, uint count) {
for (uint i = 0; i <= uint(type(Code).max); i++) {
if (votes[Code(i)] > count) {
count = votes[Code(i)];
best = Code(i);
}
}
}
}
Key Takeaways
- Structs, enums, mappings and arrays together model candidates and voters.
- Modifiers enforce the rules: only the admin publishes results; each address votes once.
- Mappings are better than array loops for “has this address already acted?” checks.
Video : Project 1 : Smart Contract for Election System
Resource Files
Question / Answer (15 questions)
blockAdmin = msg.sender;, so the deploying account becomes the admin.struct Candidate { string candidateName; string candidateParty; uint voteCount; }, defined at file level in CandidateMaster.sol and imported into VotePanelContract.sol.uint(type(candidateCode).max) + 1. The last enum member NOTA has value 4, so the total is 5.MCQ Test : Check Your Understanding (5 questions)
Select an option and press Check Answer, or answer all questions and press Submit Test to see your score. Each answer comes with an explanation.
Bibliography/References
The notes, questions and MCQs on this page are original explanations written for this course. The works listed below were consulted and are cited for attribution and further reading. Product names, logos and trademarks belong to their respective owners. Short quotations, where used, are marked and attributed.
- Solidity Team. (n.d.). Types (value types, reference types, mappings, operators, conversions). Solidity Documentation. https://docs.soliditylang.org/en/latest/types.html
- Solidity Team. (n.d.). Contracts (creating contracts, visibility and getters, function modifiers, constant and immutable, functions, events, custom errors, inheritance, abstract contracts, interfaces, libraries). Solidity Documentation. https://docs.soliditylang.org/en/latest/contracts.html
- Solidity Team. (n.d.). Security Considerations. Solidity Documentation. https://docs.soliditylang.org/en/latest/security-considerations.html
- Remix Project. (n.d.). Remix IDE Documentation. https://remix-ide.readthedocs.io/en/latest/
- ethereum.org. (n.d.). Smart contract security. Ethereum Developer Documentation. https://ethereum.org/en/developers/docs/smart-contracts/security/
- Modi, R. (2018). Solidity Programming Essentials: A beginner’s guide to build smart contracts for Ethereum and blockchain. Packt Publishing. ISBN 978-1-78883-138-3.