Local Community DAO

What it does:
Empowers local communities to self-govern, propose projects, vote on initiatives, and manage community funds transparently on-chain.

Why it matters:
Enhances civic engagement at the neighborhood or city level, ensures funds and decisions reflect community priorities, and reduces reliance on centralized authorities.

How it works:

  • Residents join the DAO and are registered with verified identities

  • Members propose local projects or initiatives with budgets and timelines

  • Voting determines which projects receive funding

  • Smart contracts manage treasury and disbursements automatically

  • Milestone tracking ensures projects are executed before funds are fully released

  • Public dashboards track proposals, votes, spending, and outcomes

  • Can integrate with reputation, referendum, and civic participation systems for enhanced governance

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

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

/**
 * @title LocalCommunityDAO
 * @author Nam
 * @notice On-chain DAO for local community governance and project funding
 */
contract LocalCommunityDAO is Ownable {

    struct Member {
        bool registered;
        string metadataURI;
    }

    struct Proposal {
        address payable proposer;
        string description;
        uint256 requestedAmount;
        uint256 votesFor;
        uint256 votesAgainst;
        bool executed;
    }

    mapping(address => Member) public members;
    mapping(uint256 => Proposal) public proposals;
    mapping(address => mapping(uint256 => bool)) public hasVoted;

    uint256 public proposalCount;

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

    event MemberRegistered(address indexed member, string metadataURI);
    event ProposalCreated(uint256 indexed proposalId, address proposer, uint256 amount);
    event Voted(uint256 indexed proposalId, address voter, bool support);
    event ProposalExecuted(uint256 indexed proposalId, uint256 amount);

    // -------------------- TREASURY --------------------

    receive() external payable {}

    // -------------------- MEMBER MANAGEMENT --------------------

    function registerMember(string calldata _metadataURI) external {
        require(!members[msg.sender].registered, "Already registered");
        members[msg.sender] = Member({
            registered: true,
            metadataURI: _metadataURI
        });
        emit MemberRegistered(msg.sender, _metadataURI);
    }

    function isMember(address _user) public view returns (bool) {
        return members[_user].registered;
    }

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

    function createProposal(string calldata _description, uint256 _requestedAmount) external {
        require(isMember(msg.sender), "Not a member");
        require(_requestedAmount > 0, "Invalid amount");
        require(address(this).balance >= _requestedAmount, "Insufficient funds");

        proposalCount += 1;
        proposals[proposalCount] = Proposal({
            proposer: payable(msg.sender),
            description: _description,
            requestedAmount: _requestedAmount,
            votesFor: 0,
            votesAgainst: 0,
            executed: false
        });

        emit ProposalCreated(proposalCount, msg.sender, _requestedAmount);
    }

    // -------------------- VOTING --------------------

    function vote(uint256 _proposalId, bool _support) external {
        require(isMember(msg.sender), "Not a member");
        require(!hasVoted[msg.sender][_proposalId], "Already voted");

        Proposal storage p = proposals[_proposalId];
        require(!p.executed, "Already executed");

        if (_support) {
            p.votesFor += 1;
        } else {
            p.votesAgainst += 1;
        }

        hasVoted[msg.sender][_proposalId] = true;
        emit Voted(_proposalId, msg.sender, _support);
    }

    // -------------------- EXECUTION --------------------

    function executeProposal(uint256 _proposalId) external {
        Proposal storage p = proposals[_proposalId];
        require(!p.executed, "Already executed");
        require(p.votesFor > p.votesAgainst, "Proposal not approved");
        require(address(this).balance >= p.requestedAmount, "Insufficient funds");

        p.executed = true;
        p.proposer.transfer(p.requestedAmount);

        emit ProposalExecuted(_proposalId, p.requestedAmount);
    }
}