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);
}
}