Community Budget Allocation

What it does:
Enables communities to propose, vote on, and fund public projects using an on-chain participatory budgeting system.

Why it matters:
Increases civic engagement, reduces corruption in public spending, and ensures community funds are allocated according to collective priorities.

How it works:

  • Community treasury holds shared funds on-chain

  • Members submit funding proposals for public projects

  • Proposals include budgets, milestones, and impact descriptions

  • Token-weighted or quadratic voting determines funding approval

  • Approved proposals receive funds automatically or by milestones

  • Spending and progress are transparently tracked on-chain

  • Governance rules can evolve through community voting

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

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

/**
 * @title CommunityBudgetAllocation
 * @author Nam
 * @notice On-chain participatory budgeting for community funds
 */
contract CommunityBudgetAllocation is Ownable {

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

    uint256 public proposalCount;
    mapping(uint256 => Proposal) public proposals;

    mapping(address => uint256) public votingPower;

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

    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 {}

    // -------------------- VOTING POWER --------------------

    function setVotingPower(address _member, uint256 _power) external onlyOwner {
        votingPower[_member] = _power;
    }

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

    function createProposal(
        string calldata _description,
        uint256 _requestedAmount
    ) external {
        require(_requestedAmount > 0, "Invalid amount");
        require(address(this).balance >= _requestedAmount, "Insufficient treasury");

        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 {
        uint256 power = votingPower[msg.sender];
        require(power > 0, "No voting power");

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

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

        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, "Not approved");
        require(address(this).balance >= p.requestedAmount, "Insufficient funds");

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

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