HOA Voting Smart Contract

What it does:
Enables homeowners in a Homeowners Association (HOA) to propose, vote, and decide on community matters such as budgets, rules, and maintenance projects through an on-chain voting system.

Why it matters:
Replaces opaque, low-participation HOA meetings with transparent, verifiable voting that increases trust, accountability, and resident engagement.

How it works:

  • Homeowners are registered with voting power based on unit ownership

  • Any eligible member can submit a proposal

  • Voting period is opened for a fixed duration

  • Votes are weighted according to ownership shares

  • Results are finalized automatically after voting ends

  • Approved proposals are recorded immutably on-chain

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

/**
 * @title HOAVoting
 * @author Nam
 * @notice On-chain voting system for Homeowners Associations
 */
contract HOAVoting {

    // -------------------- HOA ADMIN --------------------

    address public admin;

    // -------------------- HOMEOWNERS --------------------

    mapping(address => uint256) public votingPower; // based on unit ownership
    uint256 public totalVotingPower;

    // -------------------- PROPOSALS --------------------

    struct Proposal {
        string description;
        uint256 votesFor;
        uint256 votesAgainst;
        uint256 endTime;
        bool executed;
    }

    Proposal[] public proposals;
    mapping(uint256 => mapping(address => bool)) public hasVoted;

    // -------------------- EVENTS --------------------

    event HomeownerRegistered(address indexed owner, uint256 votingPower);
    event ProposalCreated(uint256 indexed proposalId, string description);
    event VoteCast(
        address indexed voter,
        uint256 indexed proposalId,
        bool support,
        uint256 weight
    );
    event ProposalExecuted(uint256 indexed proposalId);

    // -------------------- MODIFIERS --------------------

    modifier onlyAdmin() {
        require(msg.sender == admin, "Not admin");
        _;
    }

    modifier onlyHomeowner() {
        require(votingPower[msg.sender] > 0, "Not homeowner");
        _;
    }

    // -------------------- CONSTRUCTOR --------------------

    constructor() {
        admin = msg.sender;
    }

    // -------------------- HOMEOWNER MANAGEMENT --------------------

    /**
     * @notice Register a homeowner with voting power
     */
    function registerHomeowner(address _owner, uint256 _votingPower)
        external
        onlyAdmin
    {
        require(_owner != address(0), "Invalid owner");
        require(_votingPower > 0, "Invalid voting power");
        require(votingPower[_owner] == 0, "Already registered");

        votingPower[_owner] = _votingPower;
        totalVotingPower += _votingPower;

        emit HomeownerRegistered(_owner, _votingPower);
    }

    // -------------------- PROPOSAL LOGIC --------------------

    /**
     * @notice Create a new HOA proposal
     */
    function createProposal(
        string calldata _description,
        uint256 _votingDuration
    )
        external
        onlyHomeowner
    {
        proposals.push(
            Proposal({
                description: _description,
                votesFor: 0,
                votesAgainst: 0,
                endTime: block.timestamp + _votingDuration,
                executed: false
            })
        );

        emit ProposalCreated(proposals.length - 1, _description);
    }

    /**
     * @notice Vote on a proposal
     */
    function vote(uint256 _proposalId, bool _support)
        external
        onlyHomeowner
    {
        Proposal storage proposal = proposals[_proposalId];
        require(block.timestamp = proposal.endTime, "Voting ongoing");
        require(!proposal.executed, "Already executed");
        require(proposal.votesFor > proposal.votesAgainst, "Proposal rejected");

        proposal.executed = true;
        emit ProposalExecuted(_proposalId);
    }
}