Back to Projects
AdvancedFREE

Blockchain-based Voting System

Create a transparent and tamper-proof voting system on the blockchain. Learn about access control, vote delegation, time-locked voting periods, and result tabulation.

20 hours
+180 points

What You'll Learn

  • Implement secure access control patterns
  • Design voting mechanisms with delegation
  • Handle time-based voting periods
  • Ensure vote privacy and integrity
  • Build an admin dashboard for election management

Step-by-Step Guide

Step 1: Design the voting contract

Create a comprehensive voting system with proposals and vote tracking.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract VotingSystem is AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    struct Proposal {
        string title;
        string description;
        uint256 voteCount;
        uint256 startTime;
        uint256 endTime;
        bool exists;
    }

    struct Voter {
        bool registered;
        bool hasVoted;
        uint256 votedProposalId;
        address delegate;
    }

    mapping(uint256 => Proposal) public proposals;
    mapping(address => Voter) public voters;
    uint256 public proposalCount;

    event ProposalCreated(uint256 indexed id, string title, uint256 startTime, uint256 endTime);
    event VoterRegistered(address indexed voter);
    event VoteCast(address indexed voter, uint256 indexed proposalId);
    event VoteDelegated(address indexed from, address indexed to);

    constructor() {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(ADMIN_ROLE, msg.sender);
    }

    function createProposal(
        string memory _title,
        string memory _description,
        uint256 _duration
    ) external onlyRole(ADMIN_ROLE) returns (uint256) {
        uint256 proposalId = proposalCount++;

        proposals[proposalId] = Proposal({
            title: _title,
            description: _description,
            voteCount: 0,
            startTime: block.timestamp,
            endTime: block.timestamp + _duration,
            exists: true
        });

        emit ProposalCreated(proposalId, _title, block.timestamp, block.timestamp + _duration);
        return proposalId;
    }

    function registerVoter(address _voter) external onlyRole(ADMIN_ROLE) {
        require(!voters[_voter].registered, "Already registered");
        voters[_voter].registered = true;
        emit VoterRegistered(_voter);
    }

    function registerVoters(address[] calldata _voters) external onlyRole(ADMIN_ROLE) {
        for (uint i = 0; i < _voters.length; i++) {
            if (!voters[_voters[i]].registered) {
                voters[_voters[i]].registered = true;
                emit VoterRegistered(_voters[i]);
            }
        }
    }

    function delegate(address _to) external {
        Voter storage sender = voters[msg.sender];
        require(sender.registered, "Not registered");
        require(!sender.hasVoted, "Already voted");
        require(_to != msg.sender, "Cannot delegate to self");

        sender.delegate = _to;
        emit VoteDelegated(msg.sender, _to);
    }

    function vote(uint256 _proposalId) external {
        Voter storage sender = voters[msg.sender];
        require(sender.registered, "Not registered");
        require(!sender.hasVoted, "Already voted");

        Proposal storage proposal = proposals[_proposalId];
        require(proposal.exists, "Proposal does not exist");
        require(block.timestamp >= proposal.startTime, "Voting not started");
        require(block.timestamp <= proposal.endTime, "Voting ended");

        sender.hasVoted = true;
        sender.votedProposalId = _proposalId;
        proposal.voteCount++;

        emit VoteCast(msg.sender, _proposalId);
    }

    function getProposal(uint256 _id) external view returns (
        string memory title,
        string memory description,
        uint256 voteCount,
        uint256 startTime,
        uint256 endTime,
        bool isActive
    ) {
        Proposal storage p = proposals[_id];
        require(p.exists, "Proposal does not exist");

        return (
            p.title,
            p.description,
            p.voteCount,
            p.startTime,
            p.endTime,
            block.timestamp >= p.startTime && block.timestamp <= p.endTime
        );
    }
}

Prerequisites

  • Strong understanding of Solidity
  • Experience with smart contract security
  • Knowledge of access control patterns
  • Frontend development with React

Tech Stack

SolidityHardhatReactwagmiThe Graph (optional)

Resources

Track Your Progress

Sign up to save your progress and earn 180 points upon completion.