Back to Projects
IntermediateFREE

Crowdfunding App

Build a decentralized crowdfunding platform where users can create campaigns, contribute funds, and withdraw if goals are met. Learn about handling Ether, time-based conditions, and state management.

12 hours
+100 points

What You'll Learn

  • Handle Ether payments in smart contracts
  • Implement time-based campaign logic
  • Create refund mechanisms for failed campaigns
  • Build a React frontend with wagmi
  • Manage complex contract state

Step-by-Step Guide

Step 1: Design the smart contract

Create a crowdfunding contract with campaign management.

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

contract Crowdfunding {
    struct Campaign {
        address creator;
        uint256 goal;
        uint256 deadline;
        uint256 amountRaised;
        bool claimed;
        mapping(address => uint256) contributions;
    }

    uint256 public campaignCount;
    mapping(uint256 => Campaign) public campaigns;

    event CampaignCreated(uint256 indexed id, address creator, uint256 goal, uint256 deadline);
    event ContributionMade(uint256 indexed id, address contributor, uint256 amount);
    event FundsClaimed(uint256 indexed id, uint256 amount);
    event RefundClaimed(uint256 indexed id, address contributor, uint256 amount);

    function createCampaign(uint256 _goal, uint256 _duration) external returns (uint256) {
        require(_goal > 0, "Goal must be greater than 0");
        require(_duration > 0, "Duration must be greater than 0");

        uint256 campaignId = campaignCount++;
        Campaign storage campaign = campaigns[campaignId];
        campaign.creator = msg.sender;
        campaign.goal = _goal;
        campaign.deadline = block.timestamp + _duration;

        emit CampaignCreated(campaignId, msg.sender, _goal, campaign.deadline);
        return campaignId;
    }

    function contribute(uint256 _campaignId) external payable {
        Campaign storage campaign = campaigns[_campaignId];
        require(block.timestamp < campaign.deadline, "Campaign ended");
        require(msg.value > 0, "Must contribute something");

        campaign.contributions[msg.sender] += msg.value;
        campaign.amountRaised += msg.value;

        emit ContributionMade(_campaignId, msg.sender, msg.value);
    }

    function claimFunds(uint256 _campaignId) external {
        Campaign storage campaign = campaigns[_campaignId];
        require(msg.sender == campaign.creator, "Not campaign creator");
        require(block.timestamp >= campaign.deadline, "Campaign not ended");
        require(campaign.amountRaised >= campaign.goal, "Goal not reached");
        require(!campaign.claimed, "Already claimed");

        campaign.claimed = true;
        uint256 amount = campaign.amountRaised;

        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");

        emit FundsClaimed(_campaignId, amount);
    }

    function claimRefund(uint256 _campaignId) external {
        Campaign storage campaign = campaigns[_campaignId];
        require(block.timestamp >= campaign.deadline, "Campaign not ended");
        require(campaign.amountRaised < campaign.goal, "Goal was reached");

        uint256 contribution = campaign.contributions[msg.sender];
        require(contribution > 0, "No contribution");

        campaign.contributions[msg.sender] = 0;

        (bool success, ) = msg.sender.call{value: contribution}("");
        require(success, "Refund failed");

        emit RefundClaimed(_campaignId, msg.sender, contribution);
    }
}

Prerequisites

  • Completed a basic Solidity project
  • Understanding of React hooks
  • Familiarity with ethers.js or wagmi
  • Experience with async/await patterns

Tech Stack

SolidityHardhatReactwagmiTailwind CSS

Resources

Track Your Progress

Sign up to save your progress and earn 100 points upon completion.