Decentralized crowdfunding: The advantages of blockchain

Esteban Reynier
6 min readNov 1, 2023
Crowdfunding example

Introduction

Decentralized crowdfunding and blockchain technology are revolutionizing the world of fundraising, opening up new opportunities and eliminating traditional constraints. In this article, we’ll explore in detail a smart contract specifically designed for decentralized crowdfunding.

I. How the decentralized crowdfunding smart contract works

A. Overview of smart contract events and structures

The decentralized crowdfunding smart contract uses events and a structure to manage the various functionalities. “ProjectCreated”, “ProjectFinished”, “Contribution”, “Refund”, “Withdraw”, “FeeSet”, “OwnerWithdraw” and “Received” are used to notify the various actions performed on the contract.

    event ProjectCreated(uint256 indexed projectId);
event ProjectFinished(uint256 indexed projectId);
event Contribution(uint256 indexed projectId, address indexed contributor, uint256 amount);
event Refund(uint256 indexed projectId, address indexed contributor, uint256 amount);
event Withdraw(uint256 indexed projectId, address indexed owner, uint256 amount);
event FeeSet(uint256 fee);
event OwnerWithdraw(uint256 amount);
event Received(address indexed sender, uint256 amount);

The “Project” structure stores key information about each project, such as name, description, funding objective, deadline, amount raised, owner and project status.

struct Project {
string name;
string description;
uint256 goal;
uint256 deadline;
uint256 amountRaised;
address payable owner;
bool isClosed;
bool goalReached;
}

B. Explanation of global variables and constants used

The smart contract uses several global and constant variables for its operation. “fee” represents the percentage of fees collected from each contribution, “MAX_FEE” defines the maximum amount of fees allowed and set when the smart contract is deployed, “DENOMINATOR” is used to calculate the percentage fees, “feeBalance” stores the fees collected, “projects” contains all the projects created, “projectByOwner” links projects to their owner and “contributionByProject” contains the amount invested by participants.

    uint256 public fee;

uint256 public constant DENOMINATOR = 1000;

uint256 public immutable MAX_FEE;

uint256 public feeBalance;

Project[] public projects;

mapping(address => uint256[]) public projectsByOwner;

mapping(address => mapping(uint256 => uint256)) public contributionsByProject;

C. Overview of project information retrieval functions

The smart contract offers functions for retrieving project information. The “getProjectsCount” function returns the total number of projects, “getProjectsByOwner” returns the identifiers of projects created by a given owner, and “getProject” returns the details of a specific project.

function getProjectsCount() public view returns (uint256) {
return projects.length;
}

function getProjectsByOwner(address _owner) public view returns (uint256[] memory) {
return projectsByOwner[_owner];
}

function getProject(uint256 _projectId) external view returns (string memory name, string memory description, uint256 goal, uint256 deadline, uint256 amountRaised, address owner, bool isClosed, bool goalReached) {
require(_projectId < projects.length, "Project does not exist");
return (projects[_projectId].name, projects[_projectId].description, projects[_projectId].goal, projects[_projectId].deadline, projects[_projectId].amountRaised, projects[_projectId].owner, projects[_projectId].isClosed, projects[_projectId].goalReached);
}

function getContribution(address _contributor, uint256 _projectId) external view returns (uint256) {
require(_projectId < projects.length, "Project does not exist");
return contributionsByProject[_contributor][_projectId];
}

D. Explanation of smart contract configuration functions

The smart contract also features configuration functions that allow certain parameters to be modified. For example, the “setFee” function lets you define the percentage of fees deducted from each contribution, while the “ownerWithdraw” function lets the contract owner withdraw the fees collected.

function setFee(uint256 _fee) public onlyOwner {
require(_fee <= MAX_FEE, "Fee must be less than or equal to max fee");
fee = _fee;
emit FeeSet(_fee);
}

function ownerWithdraw() public onlyOwner nonReentrant {
require(feeBalance > 0, "No fee to withdraw");
uint256 amount = feeBalance;
feeBalance = 0;

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

II. Steps in the decentralized crowdfunding process

A. Creating a project

The first step in the decentralized crowdfunding process is the creation of a project. A user can use the “createProject” function to create a new project by providing the project’s name, description, funding objective and time limit. Once the project has been created, it is assigned a unique identifier and added to the list of existing projects.

function createProject(string memory _name, string memory _description, uint256 _goal, uint256 _deadline) public returns(uint256) {
require(_goal > 0, "Goal must be greater than 0");
require(_deadline >= 1 minutes, "Deadline must be at least in 1 minute");
require(projectsByOwner[msg.sender].length == 0 || projects[projectsByOwner[msg.sender][projectsByOwner[msg.sender].length - 1]].isClosed, "Users can only create one project at a time");
projects.push(Project(_name, _description, _goal, block.timestamp + _deadline, 0, payable(msg.sender), false, false));
projectsByOwner[msg.sender].push(projects.length - 1);
emit ProjectCreated(projects.length - 1);
return (projects.length - 1);
}

B. Contributions to the project

Once a project has been created, other users can contribute by sending funds to the contract. By using the “contribute” function and specifying the identifier of the project to which they wish to contribute, users can send funds to the contract. The amount of the contribution is then added to the amount collected for the project. Contributions are recorded and associated with the user and the corresponding project.

function contribute(uint256 _projectId) public payable {
require(msg.value > 0, "Contribution must be greater than 0");
require(_projectId < projects.length, "Project does not exist");
require(projects[_projectId].deadline >= block.timestamp, "Project deadline is passed");
require(!projects[_projectId].isClosed, "Project is closed");

// Take the fees
uint256 feeAmount = (msg.value * fee) / DENOMINATOR;
feeBalance += feeAmount;

// Send the rest to the project owner
projects[_projectId].amountRaised += msg.value - feeAmount;
if (projects[_projectId].amountRaised >= projects[_projectId].goal) {
projects[_projectId].goalReached = true;
}

// update contributors
contributionsByProject[msg.sender][_projectId] += msg.value - feeAmount;
emit Contribution(_projectId, msg.sender, msg.value - feeAmount);
}

C. Refund in the event of failure to achieve the objective

If the project fails to reach its funding target by the deadline, contributors can request a refund of their contribution. By using the “refund” function and specifying the project ID. The funds are transferred from the contract to the contributor’s account.

function refund(uint256 _projectId) public nonReentrant() {
require(_projectId < projects.length, "Project does not exist");
require(projects[_projectId].isClosed, "Project is not closed");
require(!projects[_projectId].goalReached, "Project goal is reached");
require(contributionsByProject[msg.sender][_projectId] > 0, "No contribution found");
uint256 amount = contributionsByProject[msg.sender][_projectId];
contributionsByProject[msg.sender][_projectId] = 0;

// safeTransfer
(bool success, ) = address(msg.sender).call{value: amount}("");
require(success, "Transfer failed");
emit Refund(_projectId, msg.sender, amount);
}

D. Withdrawal of funds if the objective is achieved

Si le projet atteint son objectif de financement avant la date limite, les fonds collectés sont disponibles pour le propriétaire du projet. En utilisant la fonction “withdraw” et en spécifiant l’identifiant du projet, le propriétaire peut retirer les fonds collectés vers son propre compte.

function withdraw(uint256 _projectId) public nonReentrant {
require(_projectId < projects.length, "Project does not exist");
require(projects[_projectId].isClosed, "Project is not closed");
require(msg.sender == projects[_projectId].owner, "Only owner can withdraw");
require(projects[_projectId].amountRaised > 0, "No amount to withdraw");
require(projects[_projectId].goalReached, "Project goal is not reached");
uint256 amount = projects[_projectId].amountRaised;
projects[_projectId].amountRaised = 0;

// safeTransfer
(bool success, ) = address(msg.sender).call{value: amount}("");
require(success, "Transfer failed");
emit Withdraw(_projectId, msg.sender, amount);
}

E. Closing a project

Marking the project as closed with the “setFinished” function allows the owner to withdraw funds or contributors to claim their refunds. The project owner can close the project at any time, while if the deadline has passed anyone can mark the project as closed. This allows a project that hasn’t reached its target to be closed by users, who can then claim their money back.

function setFinished(uint256 _projectId) public {
require(_projectId < projects.length, "Project does not exist");
require(msg.sender == projects[_projectId].owner || projects[_projectId].deadline < block.timestamp, "Only owner can set project as finished or deadline must be passed");
require(!projects[_projectId].isClosed, "Project is already closed");
projects[_projectId].isClosed = true;
emit ProjectFinished(_projectId);
}

III. Advantages of using this smart contract in decentralized crowdfunding

A. Transparency and traceability of transactions

The decentralized crowdfunding smart contract offers total transaction transparency. All contributions, repayments and withdrawals are immutably recorded on the blockchain, accessible to all participants. This provides complete visibility of financial flows and ensures trust in the crowdfunding process.

B. Transaction security and fraud prevention

Thanks to the use of blockchain, the smart contract guarantees transaction security. Funds are managed automatically and securely, eliminating the risk of manipulation or misappropriation. In addition, the use of blockchain offers protection against fraud attempts, as all transactions are verifiable and immutable.

C. Elimination of intermediaries and high fees

In traditional crowdfunding, there are often intermediaries such as crowdfunding platforms or financial institutions that levy high fees on the funds raised. With the decentralized crowdfunding smart contract, these intermediaries are eliminated, reducing costs and enabling contributors to maximize the impact of their contributions. All while making fees fully transparent.

D. Global accessibility and financial inclusion

Thanks to the decentralized nature of blockchain, crowdfunding becomes accessible to everyone, regardless of geographic location or financial status. Smart contract users can create or contribute to projects from anywhere in the world, promoting financial inclusion and offering funding opportunities to people who would otherwise have been excluded from the traditional system.

IV. Conclusion

In conclusion, decentralized crowdfunding based on blockchain technology offers many advantages that are revolutionizing the world of fundraising. Thanks to the transparency and traceability of transactions, enhanced security, the elimination of intermediaries and high fees, and global accessibility, this model of participatory financing opens up new opportunities and promotes financial inclusion. By adopting a smart contract specifically designed for decentralized crowdfunding, participants can benefit from a more efficient, transparent and trusting process, powered by blockchain.

--

--

Esteban Reynier
0 Followers

Solidity | Blockchain | Web3 Freelance Developer. Interested in audiovisual, video games and AI