Real Estate Crowdfunding DAO

What it does:
Allows multiple investors to collectively fund, own, and govern real estate assets through a DAO, with transparent capital contributions, profit distribution, and on-chain voting.

Why it matters:
Breaks down high entry barriers in real estate investing, removes reliance on centralized fund managers, and gives investors direct, trustless control over property decisions.

How it works:

A DAO is deployed for a specific real estate project with a funding target and timeline.

Investors contribute ETH and receive governance shares proportional to their contribution.

Once funding is complete, the DAO manages the property treasury.

Members vote on key decisions such as property purchase, rental strategy, or asset sale.

Profits (rent or sale proceeds) are distributed automatically based on ownership shares.

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

/**
 * @title RealEstateCrowdfundingDAO
 * @author Nam
 * @notice DAO for collective real estate investment and governance
 */
contract RealEstateCrowdfundingDAO {

    // -------------------- DAO PARAMETERS --------------------

    uint256 public fundingGoal;       // ETH
    uint256 public fundingDeadline;
    uint256 public totalContributed;
    bool public fundingClosed;

    // -------------------- GOVERNANCE --------------------

    mapping(address => uint256) public shares;
    uint256 public totalShares;

    struct Proposal {
        string description;
        uint256 votes;
        bool executed;
    }

    Proposal[] public proposals;

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

    event ContributionReceived(address indexed investor, uint256 amount);
    event ProposalCreated(uint256 indexed proposalId, string description);
    event Voted(address indexed voter, uint256 indexed proposalId, uint256 weight);
    event ProposalExecuted(uint256 indexed proposalId);
    event ProfitDistributed(uint256 amount);

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

    modifier onlyInvestor() {
        require(shares[msg.sender] > 0, "Not a DAO investor");
        _;
    }

    modifier fundingActive() {
        require(block.timestamp  0, "Zero contribution");

        shares[msg.sender] += msg.value;
        totalShares += msg.value;
        totalContributed += msg.value;

        emit ContributionReceived(msg.sender, msg.value);

        if (totalContributed >= fundingGoal) {
            fundingClosed = true;
        }
    }

    // -------------------- DAO GOVERNANCE --------------------

    /**
     * @notice Create a proposal for property decisions
     */
    function createProposal(string calldata _description)
        external
        onlyInvestor
    {
        proposals.push(
            Proposal({
                description: _description,
                votes: 0,
                executed: false
            })
        );

        emit ProposalCreated(proposals.length - 1, _description);
    }

    /**
     * @notice Vote on a proposal (weight = ownership share)
     */
    function vote(uint256 _proposalId)
        external
        onlyInvestor
    {
        Proposal storage proposal = proposals[_proposalId];
        require(!proposal.executed, "Already executed");

        uint256 weight = shares[msg.sender];
        proposal.votes += weight;

        emit Voted(msg.sender, _proposalId, weight);
    }

    /**
     * @notice Execute a proposal if majority approves
     */
    function executeProposal(uint256 _proposalId)
        external
        onlyInvestor
    {
        Proposal storage proposal = proposals[_proposalId];
        require(!proposal.executed, "Already executed");
        require(proposal.votes > totalShares / 2, "Not enough votes");

        proposal.executed = true;
        emit ProposalExecuted(_proposalId);
    }

    // -------------------- PROFIT DISTRIBUTION --------------------

    /**
     * @notice Distribute rental or sale profits to investors
     */
    function distributeProfits() external onlyInvestor {
        uint256 balance = address(this).balance;
        require(balance > 0, "No profits");

        for (uint256 i = 0; i  0, "Nothing to withdraw");

        shares[msg.sender] = 0;
        payable(msg.sender).transfer(payout);
    }
}