Creative Grant DAO

What it does:
Enables communities or organizations to fund creative projects through a DAO that proposes, votes on, and distributes grants transparently on-chain.

Why it matters:
Removes opaque grant committees, reduces favoritism, ensures fair capital allocation, and gives communities direct power to support creators they believe in.

How it works:

  • Creators submit grant proposals with funding requests and descriptions

  • DAO members stake or hold governance tokens to gain voting power

  • Proposals enter a voting period with on-chain vote tracking

  • Votes are weighted by token balance or stake

  • Approved proposals automatically receive funds from the DAO treasury

  • Grant payouts are executed trustlessly via smart contracts

  • All proposals, votes, and fund flows are transparent and auditable

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title CreativeGrantDAO
 * @author Nam
 * @notice DAO for proposing, voting, and funding creative grants on-chain
 */
contract CreativeGrantDAO is Ownable {

    IERC20 public governanceToken;

    uint256 public proposalCount;
    uint256 public votingDuration; // seconds
    uint256 public quorumPercentage; // e.g. 20 = 20%

    struct Proposal {
        address payable creator;
        string description;
        uint256 amountRequested;
        uint256 voteStart;
        uint256 voteEnd;
        uint256 votesFor;
        uint256 votesAgainst;
        bool executed;
    }

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

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

    event ProposalCreated(
        uint256 indexed proposalId,
        address indexed creator,
        uint256 amountRequested
    );

    event VoteCast(
        uint256 indexed proposalId,
        address indexed voter,
        bool support,
        uint256 weight
    );

    event ProposalExecuted(uint256 indexed proposalId);

    // -------------------- CONSTRUCTOR --------------------

    constructor(
        address _governanceToken,
        uint256 _votingDuration,
        uint256 _quorumPercentage
    ) {
        governanceToken = IERC20(_governanceToken);
        votingDuration = _votingDuration;
        quorumPercentage = _quorumPercentage;
    }

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

    receive() external payable {}

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

    function createProposal(
        string calldata _description,
        uint256 _amountRequested
    ) external {
        require(_amountRequested > 0, "Invalid amount");
        require(_amountRequested = p.voteStart, "Voting not started");
        require(block.timestamp  0, "No voting power");

        hasVoted[_proposalId][msg.sender] = true;

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

        emit VoteCast(_proposalId, msg.sender, _support, weight);
    }

    // -------------------- EXECUTION --------------------

    function executeProposal(uint256 _proposalId) external {
        Proposal storage p = proposals[_proposalId];
        require(block.timestamp > p.voteEnd, "Voting active");
        require(!p.executed, "Already executed");

        uint256 totalVotes = p.votesFor + p.votesAgainst;
        uint256 totalSupply = governanceToken.totalSupply();

        require(
            totalVotes * 100 / totalSupply >= quorumPercentage,
            "Quorum not met"
        );

        require(p.votesFor > p.votesAgainst, "Proposal rejected");

        p.executed = true;
        p.creator.transfer(p.amountRequested);

        emit ProposalExecuted(_proposalId);
    }

    // -------------------- ADMIN --------------------

    function updateVotingDuration(uint256 _duration) external onlyOwner {
        votingDuration = _duration;
    }

    function updateQuorum(uint256 _quorum) external onlyOwner {
        require(_quorum <= 100, "Invalid quorum");
        quorumPercentage = _quorum;
    }
}