CrowdFunding (solidity)
/ SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Crowdfunding {
struct Campaign {
address payable creator;
string title;
string description;
string evidence;
uint fundingGoal;
uint deadline;
uint amountRaised;
bool isCompleted;
mapping(address => uint) contributions;
}
address public owner;
uint public campaignCount;
uint public totalFeesCollected;
mapping(uint => Campaign) public campaigns;
event CampaignCreated(
uint campaignId,
address creator,
string title,
string evidence,
uint fundingGoal,
uint deadline
);
event ContributionMade(uint campaignId, address contributor, uint amount);
event FundsWithdrawn(uint campaignId, uint amountToCreator, uint feeToContract);
event ContractFeeWithdrawn(uint amount);
modifier onlyOwner() {
require(msg.sender == owner, "Only the owner can perform this action");
_;
}
modifier isCampaignActive(uint _campaignId) {
Campaign storage campaign = campaigns[_campaignId];
require(block.timestamp < campaign.deadline, "Campaign deadline passed");
require(!campaign.isCompleted, "Campaign is completed");
_;
}
constructor() {
owner = msg.sender;
}
function createCampaign(
string memory _title,
string memory _description,
string memory _evidence,
uint _fundingGoal,
uint _duration
) external {
require(_fundingGoal > 0, "Funding goal must be greater than 0");
require(_duration > 0, "Duration must be greater than 0");
Campaign storage newCampaign = campaigns[campaignCount];
newCampaign.creator = payable(msg.sender);
newCampaign.title = _title;
newCampaign.description = _description;
newCampaign.evidence = _evidence;
newCampaign.fundingGoal = _fundingGoal;
newCampaign.deadline = block.timestamp + _duration;
emit CampaignCreated(campaignCount, msg.sender, _title, _evidence, _fundingGoal, newCampaign.deadline);
campaignCount++;
}
function contribute(uint _campaignId) external payable isCampaignActive(_campaignId) {
Campaign storage campaign = campaigns[_campaignId];
require(msg.value > 0, "Contribution must be greater than 0");
require(campaign.amountRaised < campaign.fundingGoal, "Funding goal already reached");
campaign.contributions[msg.sender] += msg.value;
campaign.amountRaised += msg.value;
emit ContributionMade(_campaignId, msg.sender, msg.value);
}
function withdrawFunds(uint _campaignId) external {
Campaign storage campaign = campaigns[_campaignId];
require(msg.sender == campaign.creator, "Only the creator can withdraw funds");
require(!campaign.isCompleted, "Funds already withdrawn");
require(
block.timestamp >= campaign.deadline || campaign.amountRaised >= campaign.fundingGoal,
"Campaign is still active"
);
uint fee = (campaign.amountRaised * 2) / 100; // 2% fee
uint amountToCreator = campaign.amountRaised - fee;
campaign.isCompleted = true;
totalFeesCollected += fee;
(bool success, ) = campaign.creator.call{value: amountToCreator}("");
require(success, "Transfer to creator failed");
emit FundsWithdrawn(_campaignId, amountToCreator, fee);
}
function withdrawContractFees() external onlyOwner {
require(totalFeesCollected > 0, "No fees available to withdraw");
uint amount = totalFeesCollected;
totalFeesCollected = 0;
(bool success, ) = owner.call{value: amount}("");
require(success, "Transfer to owner failed");
emit ContractFeeWithdrawn(amount);
}
function getCampaignDetails(uint _campaignId) external view returns (
address creator,
string memory title,
string memory description,
string memory evidence,
uint fundingGoal,
uint deadline,
uint amountRaised,
bool isCompleted
) {
Campaign storage campaign = campaigns[_campaignId];
return (
campaign.creator,
campaign.title,
campaign.description,
campaign.evidence,
campaign.fundingGoal,
campaign.deadline,
campaign.amountRaised,
campaign.isCompleted
);
}}
💰 Solidity Smart Contract: Crowdfunding
📍Deployed on Polygon Testnet
This contract enables decentralized crowdfunding with campaign creation, contribution handling, and fee-based fund withdrawals.
✅ License & Version
// SPDX-License-Identifier: MIT
- Declares the open-source license (MIT).
pragma solidity ^0.8.0;
- Requires Solidity version 0.8.0 or higher.
- Ensures compatibility and access to the latest features and safety improvements.
🏗️ Contract Declaration
contract Crowdfunding {
- Defines a contract named
Crowdfunding.
🧱 Struct: Campaign
struct Campaign {
address payable creator;
string title;
string description;
string evidence;
uint fundingGoal;
uint deadline;
uint amountRaised;
bool isCompleted;
mapping(address => uint) contributions;
}
Defines the structure for a single campaign:
| Field | Type | Description |
|---|---|---|
creator |
address payable |
Campaign owner, allowed to receive funds. |
title |
string |
Campaign title. |
description |
string |
Description of the campaign. |
evidence |
string |
Proof or evidence (e.g., IPFS link, image URL). |
fundingGoal |
uint |
Funding target in wei. |
deadline |
uint |
Campaign end time (timestamp). |
amountRaised |
uint |
Amount contributed so far. |
isCompleted |
bool |
True once funds have been withdrawn. |
contributions |
mapping |
Tracks how much each address has contributed. |
🧠 Note: mapping inside a struct cannot be returned directly — it’s internal storage only.
📦 Global Variables
address public owner;
uint public campaignCount;
uint public totalFeesCollected;
mapping(uint => Campaign) public campaigns;
owner: Contract deployer; can withdraw platform fees.campaignCount: Tracks number of campaigns.totalFeesCollected: 2% fee from campaigns.campaigns: Maps a campaign ID (uint) to its data (Campaignstruct).
📣 Events
event CampaignCreated(...);
event ContributionMade(...);
event FundsWithdrawn(...);
event ContractFeeWithdrawn(...);
These allow external interfaces (like dApps) to listen for changes:
| Event | Description |
|---|---|
CampaignCreated |
Triggered when a new campaign is made. |
ContributionMade |
Logs a new contribution. |
FundsWithdrawn |
Logs fund withdrawal by a creator. |
ContractFeeWithdrawn |
Logs withdrawal of contract fees by the owner. |
🔐 Access Control Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Only the owner can perform this action");
_;
}
- Restricts a function to the contract
owner.
modifier isCampaignActive(uint _campaignId) {
Campaign storage campaign = campaigns[_campaignId];
require(block.timestamp < campaign.deadline, "Campaign deadline passed");
require(!campaign.isCompleted, "Campaign is completed");
_;
}
- Ensures a campaign is still running before allowing contributions.
🏁 Constructor
constructor() {
owner = msg.sender;
}
- Sets the contract deployer as
owner.
🆕 Function: createCampaign(...)
function createCampaign(...) external { ... }
| Parameter | Type | Description |
|---|---|---|
_title |
string | Campaign name. |
_description |
string | Campaign details. |
_evidence |
string | Link or hash to proof (images, documents). |
_fundingGoal |
uint | Minimum funding target in wei. |
_duration |
uint | Campaign length in seconds. |
Steps:
- Validates goal and duration.
- Initializes a new campaign in the
campaignsmapping. - Emits
CampaignCreated.
💸 Function: contribute(...)
function contribute(uint _campaignId) external payable isCampaignActive(_campaignId)
- Requires payment (
payable) and checks if the campaign is active. - Verifies non-zero contribution and funding goal isn’t already reached.
- Adds contribution to campaign’s
amountRaised. - Tracks how much each sender contributed.
- Emits
ContributionMade.
🏦 Function: withdrawFunds(...)
function withdrawFunds(uint _campaignId) external
Only callable by campaign creator if:
- Campaign is complete (deadline passed or goal reached).
- Campaign not already withdrawn.
Fee Structure:
- 2% platform fee taken from total raised.
- Remaining 98% sent to the creator using
call{value: ...}(modern recommended pattern).
Updates:
- Marks campaign as
isCompleted = true. - Adds fee to
totalFeesCollected. - Emits
FundsWithdrawn.
🧾 Function: withdrawContractFees()
function withdrawContractFees() external onlyOwner
- Lets contract
ownercollect accumulated 2% fees. - Uses
callto transfer fees. - Emits
ContractFeeWithdrawn.
🔍 Function: getCampaignDetails(...)
function getCampaignDetails(...) external view returns (...)
Returns read-only information about a campaign, excluding internal mappings:
creatortitledescriptionevidencefundingGoaldeadlineamountRaisedisCompleted
Useful for front-end UIs.
📚 Summary Table
| Concept | Description |
|---|---|
struct |
Used to model a campaign’s data. |
mapping |
Associates campaign IDs with campaigns, and contributors with amounts. |
modifier |
Reusable conditions (like access control). |
event |
Emits logs for external applications to track changes. |
call{value: ...} |
Secure and flexible Ether transfer method. |
block.timestamp |
Current time used to compare against deadlines. |
payable |
Marks functions or addresses that can send/receive Ether. |