Journalism Funding DAO

What it does:
Enables communities to collectively fund, govern, and reward independent journalism through transparent, on-chain proposals, voting, and milestone-based payouts.

Why it matters:
Reduces dependence on advertisers or centralized sponsors, protects editorial independence, aligns journalists with readers, and makes funding decisions auditable and trust-minimized.

How it works:

  • Journalists submit funding proposals describing investigations, budgets, and timelines

  • DAO members fund the treasury and gain voting power through tokens or contributions

  • Members vote on which journalism proposals to fund

  • Approved proposals receive funds in tranches or milestones, not lump sums

  • Journalists submit progress updates or proofs of work on-chain or via references

  • Funds are released automatically when milestones are approved

  • All funding, voting, and payouts are transparent and auditable on-chain

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

/**
 * @title JournalismFundingDAO
 * @author Nam
 * @notice DAO for funding journalism via proposals, voting, and milestone-based payouts
 */
contract JournalismFundingDAO {

    uint256 public proposalCount;
    uint256 public memberCount;

    // -------------------- STRUCTS --------------------

    struct Member {
        bool active;
        uint256 votingPower;
    }

    struct Milestone {
        uint256 amount;
        bool released;
    }

    struct Proposal {
        address payable journalist;
        string description;
        uint256 yesVotes;
        uint256 noVotes;
        uint256 deadline;
        bool approved;
        Milestone[] milestones;
    }

    // -------------------- STORAGE --------------------

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

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

    event MemberJoined(address indexed member, uint256 votingPower);
    event ProposalSubmitted(uint256 indexed proposalId, address indexed journalist);
    event Voted(uint256 indexed proposalId, address indexed voter, bool support, uint256 weight);
    event ProposalApproved(uint256 indexed proposalId);
    event MilestoneReleased(uint256 indexed proposalId, uint256 milestoneIndex, uint256 amount);

    // -------------------- MODIFIERS --------------------

    modifier onlyMember() {
        require(members[msg.sender].active, "Not a member");
        _;
    }

    // -------------------- MEMBERSHIP --------------------

    function joinDAO(uint256 _votingPower) external {
        require(!members[msg.sender].active, "Already member");
        require(_votingPower > 0, "Invalid voting power");

        members[msg.sender] = Member({
            active: true,
            votingPower: _votingPower
        });

        memberCount += 1;
        emit MemberJoined(msg.sender, _votingPower);
    }

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

    function submitProposal(
        string calldata _description,
        uint256 _votingDuration,
        uint256[] calldata _milestoneAmounts
    ) external {
        require(_milestoneAmounts.length > 0, "No milestones");

        proposalCount += 1;
        Proposal storage p = proposals[proposalCount];
        p.journalist = payable(msg.sender);
        p.description = _description;
        p.deadline = block.timestamp + _votingDuration;

        for (uint256 i = 0; i < _milestoneAmounts.length; i++) {
            p.milestones.push(
                Milestone({
                    amount: _milestoneAmounts[i],
                    released: false
                })
            );
        }

        emit ProposalSubmitted(proposalCount, msg.sender);
    }

    function vote(uint256 _proposalId, bool _support) external onlyMember {
        Proposal storage p = proposals[_proposalId];
        require(block.timestamp <p>= p.deadline, "Voting ongoing");
        require(!p.approved, "Already finalized");

        require(p.yesVotes &gt; p.noVotes, "Proposal rejected");
        p.approved = true;

        emit ProposalApproved(_proposalId);
    }

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

    receive() external payable {}

    // -------------------- MILESTONE PAYOUTS --------------------

    function releaseMilestone(uint256 _proposalId, uint256 _milestoneIndex)
        external
        onlyMember
    {
        Proposal storage p = proposals[_proposalId];
        require(p.approved, "Not approved");
        require(_milestoneIndex <p>= m.amount, "Insufficient treasury");

        m.released = true;
        p.journalist.transfer(m.amount);

        emit MilestoneReleased(_proposalId, _milestoneIndex, m.amount);
    }
}