NGO Fund Management Contract

What it does:
Manages donations, grants, and fund disbursement for NGOs on-chain, ensuring transparency, proper allocation, and auditable reporting.

Why it matters:
Increases donor trust, prevents misuse of funds, and allows all stakeholders to track how donations are distributed and spent.

How it works:

  • NGO registers on-chain with metadata about mission and projects

  • Donors deposit funds into the NGO treasury

  • Proposals for fund usage are submitted with purpose, amount, and project details

  • DAO members, auditors, or board vote to approve proposals

  • Smart contract releases funds automatically upon approval

  • All deposits, votes, and fund disbursements are recorded immutably on-chain

  • Public dashboards can track donations, fund usage, and project progress

  • Can integrate with milestone-based funding, transparency dashboards, and reporting systems

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

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

/**
 * @title NGOFundManagement
 * @author Nam
 * @notice Manages NGO funds with on-chain proposals, voting, and transparent disbursement
 */
contract NGOFundManagement is Ownable {

    struct NGO {
        string name;
        string metadataURI;
        bool registered;
    }

    struct Proposal {
        address payable recipient;
        string purpose;
        uint256 amount;
        uint256 votesFor;
        uint256 votesAgainst;
        bool executed;
    }

    mapping(address => NGO) public ngos;
    mapping(uint256 => Proposal) public proposals;
    mapping(address => mapping(uint256 => bool)) public hasVoted;
    mapping(address => bool) public auditors;

    uint256 public proposalCount;

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

    event NGORegistered(address indexed ngo, string name);
    event DonationReceived(address indexed donor, uint256 amount);
    event ProposalCreated(uint256 indexed proposalId, address recipient, uint256 amount);
    event Voted(uint256 indexed proposalId, address voter, bool support);
    event ProposalExecuted(uint256 indexed proposalId, uint256 amount);
    event AuditorApproved(address auditor);

    // -------------------- NGO MANAGEMENT --------------------

    function registerNGO(string calldata _name, string calldata _metadataURI) external {
        require(!ngos[msg.sender].registered, "Already registered");
        ngos[msg.sender] = NGO({
            name: _name,
            metadataURI: _metadataURI,
            registered: true
        });
        emit NGORegistered(msg.sender, _name);
    }

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

    // -------------------- AUDITOR MANAGEMENT --------------------

    function approveAuditor(address _auditor) external onlyOwner {
        auditors[_auditor] = true;
        emit AuditorApproved(_auditor);
    }

    function revokeAuditor(address _auditor) external onlyOwner {
        auditors[_auditor] = false;
    }

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

    receive() external payable {
        emit DonationReceived(msg.sender, msg.value);
    }

    // -------------------- PROPOSAL MANAGEMENT --------------------

    function createProposal(address payable _recipient, string calldata _purpose, uint256 _amount) external {
        require(isNGO(msg.sender), "Only NGOs can create proposals");
        require(_amount > 0, "Amount must be >0");
        require(address(this).balance >= _amount, "Insufficient funds");

        proposalCount += 1;
        proposals[proposalCount] = Proposal({
            recipient: _recipient,
            purpose: _purpose,
            amount: _amount,
            votesFor: 0,
            votesAgainst: 0,
            executed: false
        });

        emit ProposalCreated(proposalCount, _recipient, _amount);
    }

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

    function vote(uint256 _proposalId, bool _support) external {
        require(auditors[msg.sender], "Not an authorized auditor");
        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.amount, "Insufficient funds");

        p.executed = true;
        p.recipient.transfer(p.amount);
        emit ProposalExecuted(_proposalId, p.amount);
    }
}