Public Project Milestone Tracking
What it does:
Tracks public project milestones and automates fund disbursement based on verified completion of milestones.
Why it matters:
Ensures accountability, reduces fund misuse, and provides transparent, auditable progress for taxpayers or DAO members funding public initiatives.
How it works:
-
Project proposals are submitted with milestones, budgets, and deadlines
-
DAO or authorized auditors verify milestone completion
-
Smart contract releases funds automatically upon verification
-
Milestone progress and fund usage are logged immutably on-chain
-
Public dashboards visualize project progress and financial flows
-
Integrates with DAOs, Smart Tax Allocation, and Community Budget Allocation systems
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title PublicProjectMilestoneTracking
* @author Nam
* @notice Tracks public project milestones and releases funds based on completion verification
*/
contract PublicProjectMilestoneTracking is Ownable {
struct Milestone {
string description;
uint256 amount; // amount allocated for milestone
bool completed;
}
struct Project {
address payable proposer;
string description;
Milestone[] milestones;
uint256 totalBudget;
uint256 releasedAmount;
}
mapping(uint256 => Project) public projects;
uint256 public projectCount;
mapping(address => bool) public auditors;
// -------------------- EVENTS --------------------
event AuditorApproved(address auditor);
event ProjectSubmitted(uint256 indexed projectId, address proposer, uint256 totalBudget);
event MilestoneCompleted(uint256 indexed projectId, uint256 milestoneIndex, uint256 amountReleased);
// -------------------- AUDITOR MANAGEMENT --------------------
function approveAuditor(address _auditor) external onlyOwner {
auditors[_auditor] = true;
emit AuditorApproved(_auditor);
}
function revokeAuditor(address _auditor) external onlyOwner {
auditors[_auditor] = false;
}
// -------------------- PROJECT SUBMISSION --------------------
function submitProject(string calldata _description, string[] calldata _milestoneDescriptions, uint256[] calldata _milestoneAmounts) external payable {
require(_milestoneDescriptions.length == _milestoneAmounts.length, "Milestone data mismatch");
uint256 totalBudget = 0;
for (uint256 i = 0; i < _milestoneAmounts.length; i++) {
totalBudget += _milestoneAmounts[i];
}
require(msg.value == totalBudget, "Incorrect funding sent");
projectCount += 1;
Project storage p = projects[projectCount];
p.proposer = payable(msg.sender);
p.description = _description;
p.totalBudget = totalBudget;
p.releasedAmount = 0;
for (uint256 i = 0; i < _milestoneDescriptions.length; i++) {
p.milestones.push(Milestone({
description: _milestoneDescriptions[i],
amount: _milestoneAmounts[i],
completed: false
}));
}
emit ProjectSubmitted(projectCount, msg.sender, totalBudget);
}
// -------------------- MILESTONE VERIFICATION --------------------
function completeMilestone(uint256 _projectId, uint256 _milestoneIndex) external {
require(auditors[msg.sender], "Not authorized auditor");
Project storage p = projects[_projectId];
require(_milestoneIndex < p.milestones.length, "Invalid milestone index");
Milestone storage m = p.milestones[_milestoneIndex];
require(!m.completed, "Already completed");
m.completed = true;
p.releasedAmount += m.amount;
p.proposer.transfer(m.amount);
emit MilestoneCompleted(_projectId, _milestoneIndex, m.amount);
}
// -------------------- VIEW FUNCTIONS --------------------
function getMilestones(uint256 _projectId) external view returns (Milestone[] memory) {
return projects[_projectId].milestones;
}
}