本章讲解什么是众筹合约,以及众筹合约的算法和实现。
官网:BinSchoolOrg
众筹合约是帮助众筹项目筹集资金的智能合约。项目创建者需要发布一个众筹活动的合约,然后请求用户向合约捐款,以实现项目的筹集目标。
捐款人可以参与众筹活动,向项目提供资金支持。而项目创建者在达成众筹目标之后,可以提取合约中筹集的资金。
一个完整的众筹合约至少包含以下函数和数据:
合约的构造函数 constructor
用于初始化众筹项目的初始状态。构造函数通常需要设置一个目标金额,并将合约部署者的地址记录为项目创建者。
只有项目创建者才能提取合约中的资金。
合约提供了一个名为 contribute
的外部函数,允许用户捐款。
捐款人可以通过这个函数向众筹项目捐款。
合约会记录每位捐款人的捐款金额,并发出捐款事件以记录捐款人和金额。
如果已筹集金额达到或超过目标金额,众筹合约将会被标记为已关闭。
合约提供了一个名为 withdraw
的外部函数,该函数用于将合约中已筹集的资金提取到项目创建者的地址。
这个函数只有项目创建者可以执行,其它用户没有权限。
另外,还有一些其它限制条件,比如:达到众筹总额等等。
合约提供了一个名为 getContributionAmount
的外部函数,用于查询某位捐款人的捐款金额。
合约包含多个状态变量来存储项目信息,例如:项目创建者、目标金额、已筹集金额、众筹状态和总捐款人数。
合约还定义了两个事件,FundingReceived
用于记录捐款事件,ProjectClosed
用于记录众筹关闭事件。
这两个事件可以提供给外部程序进行监控或者查询统计。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CrowdFunding {
address public creator; // 众筹项目创建者地址
uint256 public goalAmount; // 众筹的目标金额
uint256 public raisedAmount; // 已筹集的金额
bool public closed; // 众筹是否已关闭
// 存储总捐款人数
uint256 public totalContributorsCount;
// 存储每位捐款人的捐款金额
mapping(address => uint256) public contributions;
// 捐款事件,记录捐款人和金额
event FundingReceived(address indexed contributor,
uint256 amount);
// 项目关闭事件,记录总筹集金额
event ProjectClosed(uint256 totalAmountRaised);
// 构造函数,初始化项目创建者和目标金额
constructor(uint256 _goalAmount) {
// 将部署合约的用户地址设为项目创建者
creator = msg.sender;
// 设置众筹的目标金额
goalAmount = _goalAmount;
// 初始化已筹集的金额为零
raisedAmount = 0;
// 初始化众筹状态为未关闭
closed = false;
}
// 捐款函数,允许用户捐款
function contribute() external payable {
// 检查捐款金额必须大于零
require(msg.value > 0,
"Donation amount must be greater than zero");
// 检查众筹必须尚未关闭
require(!closed, "Crowdfunding is already closed");
// 增加已筹集的金额
raisedAmount += msg.value;
// 发出捐款事件,记录捐款人和金额
emit FundingReceived(msg.sender, msg.value);
// 统计总捐款人数,剔除重复捐款者
if (contributions[msg.sender] == 0) {
totalContributorsCount++;
}
// 存储捐款人的捐款金额
contributions[msg.sender] += msg.value;
if (raisedAmount >= goalAmount) {
// 如果已筹集金额达到或超过目标金额,将众筹标记为已关闭
closed = true;
// 发出众筹关闭事件,记录总筹集金额
emit ProjectClosed(raisedAmount);
}
}
// 获取指定捐款人的捐款金额
function getContributionAmount(address contributor)
external view returns (uint256) {
// 从状态变量 contributions 中,查询指定地址的金额
return contributions[contributor];
}
// 提取筹集的资金,只有项目创建者可以执行
function withdraw() external {
// 检查众筹必须已关闭
require(closed, "Crowdfunding is not closed yet");
// 检查只有项目创建者可以提取资金
require(msg.sender == creator,
"Only the project creator can withdraw funds");
// 检查捐款金额是否大于0
require(raisedAmount > 0,
"Raiseed amount must be greater than zero");
// 向项目创建者转移合约中的全部资金
payable(creator).transfer(raisedAmount);
// 将已筹集的金额重置为零
raisedAmount = 0;
}
}
我们可以把上面编写的众筹合约,复制到 Remix
里进行编译,然后部署到区块链上。
第一步:部署合约的时候需要填写众筹目标金额,比如:填写为 300 wei
。
合约部署后,我们会看到各个状态变量值都已经初始化,其中筹集目标 goalAmount
变为 300 wei
。
第二步:我们通过 contribute
开始捐款,先填写捐款金额,比如 100 wei
, 然后点击 contribute
。
我们可以不断捐款,直至达到或超过目标金额。
第三步:当达到目标金额后,我们可以查看它的状态,close
变为 true
,表示众筹完成。
我们就可以通过 withdraw
提走筹集的资金。