[
]
Welcome, blockchain enthusiasts and crowdfunding revolutionaries! Today, we’re diving deep into a smart contract that’s set to transform the way we fund great ideas — the CrowdFunding contract on BitTorrent Chain (BTTC). This powerful piece of code brings transparency, efficiency, and trust to the world of crowdfunding. Let’s unpack this digital marvel and see how it works!
Imagine a world where crowdfunding campaigns are transparent, secure, and free from intermediaries. That’s exactly what our CrowdFunding contract achieves. It’s a self-contained ecosystem for creating, managing, and contributing to fundraising campaigns on the blockchain.
The contract starts by defining the essential variables and structures needed to manage crowdfunding campaigns:
// Address of the contract owner
address private immutable owner;// Counter for generating campaign IDs
uint private nextId = 1;
// Array to store campaign information
Campaign[] public campaigns;
// Flag to prevent reentrant calls
bool private locked;
// Modifier to prevent reentrant calls
modifier nonReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
// Struct to define the structure of a crowdfunding campaign
struct Campaign {
uint id;
address campaignCreator;
string title;
string description;
string imageURI;
uint goal;
uint startsAt;
uint endsAt;
STATUS status;
uint totalContributions;
address[] contributors;
uint[] contributionAmounts;
}
// Enum representing the status of a campaign
enum STATUS {
ACTIVE,
DELETED,
SUCCESSFUL,
UNSUCCEEDED
}
These variables and structs form the backbone of our contract:
– owner: The address that deployed the contract.
– nextId: A counter for generating unique campaign IDs.
– campaigns: An array storing all campaign information.
– Campaign: A struct defining the structure of each crowdfunding campaign.
– STATUS: An enum representing the possible states of a campaign.
Events are crucial for transparency and off-chain tracking:
// Events to log important activities
event CampaignCreated(uint indexed campaignId, address campaignCreator, string title, STATUS status);
event CampaignDeleted(uint indexed campaignId, address campaignCreator, STATUS status);
event ContributionMade(uint indexed campaignId, address contributor, uint amount);
event RefundMade(uint indexed campaignId, address contributor, uint amount);
These events help in tracking the lifecycle of campaigns and contributions:
– CampaignCreated: Fired when a new campaign is launched.
– CampaignDeleted: Emitted when a campaign is deleted.
– ContributionMade: Logs each contribution to a campaign.
– RefundMade: Records when a refund is processed.
The constructor sets the deployer of the contract as the owner, ensuring that only the owner can perform certain administrative actions:
constructor() {
owner = msg.sender;
}
Creating a Campaign
This function allows anyone to create a new crowdfunding campaign:
function createCampaign(
string memory title,
string memory description,
string memory imageURI,
uint goal,
uint endsAt
) public {
// Validation checks for input parameters
require(bytes(title).length > 0, 'Title must not be empty');
require(bytes(description).length > 0, 'Description must not be empty');
require(bytes(imageURI).length > 0, 'Image URI must not be empty');
require(goal > 0, 'Goal must be greater than zero');
require(endsAt > block.timestamp, 'Ends time must be greater than the current time');// Create a new campaign and add it to the array
campaigns.push(Campaign(
nextId++,
msg.sender,
title,
description,
imageURI,
goal,
block.timestamp,
endsAt,
STATUS.ACTIVE,
0,
new address[](0),
new uint[](0)
));
// Emit an event to log the creation of the campaign
emit CampaignCreated(nextId - 1, msg.sender, title, STATUS.ACTIVE);
}
Contributing to a Campaign
Here’s where the magic happens! Contributors can send funds to support a campaign:
function contribute(uint campaignId) public payable nonReentrant {
// Retrieve the campaign based on the campaign ID
Campaign storage campaign = campaigns[campaignId];// Validation check for contribution amount
require(msg.value > 0, 'Contribution amount must be greater than zero');
// Calculate the remaining funds needed to reach the campaign goal
uint remainingFundsNeeded = campaign.goal - campaign.totalContributions;
// Handle contributions based on the remaining funds needed
if (msg.value <= remainingFundsNeeded) {
campaign.totalContributions += msg.value;
} else {
// Handle excess contributions and refunds
uint excessAmount = msg.value - remainingFundsNeeded;
uint refundedAmount = msg.value - excessAmount;
// Refund the excess amount to the contributor
payable(msg.sender).transfer(excessAmount);
// Update the total contributions with the refunded amount
campaign.totalContributions += refundedAmount;
// Mark the campaign as successful if the goal is reached
if (campaign.totalContributions >= campaign.goal) {
campaign.status = STATUS.SUCCESSFUL;
}
}
// Record the contributor and contribution amount
campaign.contributors.push(msg.sender);
campaign.contributionAmounts.push(msg.value);
// Emit an event to log the contribution
emit ContributionMade(campaignId, msg.sender, msg.value);
}
Deleting a Campaign
Campaign creators can use this function to delete their campaigns:
function deleteCampaign(uint campaignId) public {
// Retrieve the campaign based on the campaign ID
Campaign storage campaign = campaigns[campaignId];// Validation check: Only the campaign creator can delete the campaign
require(campaign.campaignCreator == msg.sender);
// Refund contributors when the campaign is deleted
refund(campaignId);
// Mark the campaign as deleted
campaign.status = STATUS.DELETED;
// Emit an event to log the deletion of the campaign
emit CampaignDeleted(campaignId, msg.sender, STATUS.DELETED);
}
Internal Refund Function
This internal function handles the refund process when a campaign is deleted:
function refund(uint campaignId) internal {
// Retrieve the campaign based on the campaign ID
Campaign storage campaign = campaigns[campaignId];// Iterate through contributors and refund their contributions
for (uint i = 0; i < campaign.contributors.length; i++) {
address contributor = campaign.contributors[i];
uint contributionAmount = campaign.contributionAmounts[i];
// Refund the contribution amount to the contributor
payable(contributor).transfer(contributionAmount);
// Update the total contributions
campaign.totalContributions -= contributionAmount;
// Emit an event to log the refund
emit RefundMade(campaignId, contributor, contributionAmount);
}
}
Retrieving Campaign Information
These functions provide various ways to retrieve campaign information:
function getAllCampaigns() public view returns (Campaign[] memory) {
return campaigns;
}function getCampaignDetails(uint campaignId) public view returns (
uint id,
address campaignCreator,
string memory title,
string memory description,
string memory imageURI,
uint goal,
uint startsAt,
uint endsAt,
STATUS status,
uint totalContributions,
address[] memory contributors,
uint[] memory contributionAmounts
) {
Campaign memory campaign = campaigns[campaignId];
return (
campaign.id,
campaign.campaignCreator,
campaign.title,
campaign.description,
campaign.imageURI,
campaign.goal,
campaign.startsAt,
campaign.endsAt,
campaign.status,
campaign.totalContributions,
campaign.contributors,
campaign.contributionAmounts
);
}
function getTotalContributions(uint campaignId) public view returns (uint) {
return campaigns[campaignId].totalContributions;
}
function getLatestCampaigns() public view returns (Campaign[] memory) {
require(campaigns.length > 0, "No campaigns found.");
// Determine the start index based on the number of campaigns
uint startIndex = campaigns.length > 4 ? campaigns.length - 4 : 0;
uint latestCampaignsCount = campaigns.length - startIndex;
// Create an array for the latest campaigns
Campaign[] memory latestCampaigns = new Campaign[](latestCampaignsCount);
// Populate the array with the latest campaigns
for (uint i = 0; i < latestCampaignsCount; i++) {
latestCampaigns[i] = campaigns[campaigns.length - 1 - i];
}
return latestCampaigns;
}
This CrowdFunding contract opens up a world of possibilities:
- Transparency: All transactions and campaign details are visible on the blockchain.
- Security: Smart contract logic ensures funds are handled correctly.
- Global Reach: Anyone with an internet connection can create or contribute to campaigns.
- Automated Refunds: Built-in mechanisms for handling campaign deletions and refunds.
The CrowdFunding smart contract is more than just code
– it’s a glimpse into the future of fundraising. By leveraging blockchain technology, we’re creating a more transparent, efficient, and accessible crowdfunding ecosystem.
As you explore the potential of this contract on BTTC, remember: you’re not just participating in campaigns, you’re pioneering a new era of decentralized finance and community support.
So, what groundbreaking idea will you fund first? The blockchain is waiting, and the crowd is ready to back the next big thing!
Github URL:
https://github.com/adeelch9/bttc-examples/tree/master/projects/crowdfunding
[