1.创建众筹项目
在这里我们将众筹项目使用结构体来封装起来,可以记录下该项目创建的地址,给我们的众筹项目起个标题,增加描述,这个众筹的钱是拿来干嘛的,还可以记录我们的目标金额以及筹到的金额,项目开始的时间以及结束的时间,时间结束之后钱是否被提取,以及参与众筹的人数。一下代码就是项目的结构体
struct _Project {
address creator;
string title;
string des;
uint256 goalMoney;
uint256 pledged;
uint256 startTime;
uint256 endTime;
bool extracted;
uint256 projectPeople;
}
2.参与众筹
我们要创建一个函数,用来发起众筹,用户可以根据自己的需求来创建众筹的项目,但是在创建时会有一些限制性的条件。比如项目的结束时间不能比开始时间早(我们用时间戳来比较)、需要筹的钱不能小于项目要求的数额、同一个人是不能重复捐款的、判断时间是否还在规定的范围内,在捐款之后我们要记录下捐款人的金额、时间以及将它标记为捐过的状态,同时给捐款的人给予一个ERC20的代币奖励
//用户参与众筹
function pledge(uint256 _id) public payable{
Project storage project = projects[_id];
require(block.timestamp >= project.startTime, "not start");
require(block.timestamp <= project.endTime, "end");
require(msg.value >= 2 , "amount<2eth"); //不低于2eth
require(msg.value + project.pledged <= project.goalMoney* 1e18, "amount>goalMoney");
require(!contributors[msg.sender], "already contribut"); // 判断是否已经付款
require(
contributionTimes[_id][msg.sender] <= block.timestamp,
"too soon to contribute again"
);
project.pledged +=msg.value;
pledgedMoney[_id][msg.sender] += msg.value;
contributors[msg.sender] = true; // 标记为已付款
contributionTimes[_id][msg.sender] = block.timestamp; // 记录付款时间
project.projectPeople++; //计算参与众筹的人数
token.transfer(msg.sender, 1); // 发送1个代币给捐款人
emit Pledge(_id, msg.sender, msg.value);
}
3.众筹项目查询
由于运用了一个映射来存储众筹合约的内容,这里的uint256也就代表着每个项目的编号(id),输入相应的编号,我们能看见该项目的捐款人数、捐款金额以及目标金额等等
mapping(uint256 =>Project) public projects;
4.提取资金
定义一个函数来提取众筹成功后的金额,在提取前也会有要求,看提取人是否在可提取余额的账户列表之内,如果在则可以提取,同时也要满足众筹的金额满足目标的金额,以及众筹的时间已经结束,提取之后会将该地址赋予一个标记,以防止重复提取金额,之后再触发事件
//发起众筹的人当要求满足时可以取出金额
function extracted(uint256 _id) external {
Project storage project = projects[_id];
for (uint256 i = 0; i < accounts.length; i++) {
// 检查账户是否在可用提取的账户列表中
require(
contains(accounts, msg.sender),
"Account not found in the list of available accounts"
);
}
// require(block.timestamp > project.endTime, "not ended");
require(project.pledged >= project.goalMoney* 1e18, "pledged < goal");
require(!project.extracted, "claimed");
project.extracted = true;
payable(msg.sender).transfer(project.pledged);
emit Extracted(_id);
}
5.退款机制
如果众筹项目未达到目标金额,在截止日期到达后,参与者可以请求退款。
//当资金未满可以取消参与的众筹
function unpledge(uint256 _id) external payable {
Project storage project = chengjinjin_projects[_id];
require(block.timestamp <= project.endTime, "ended");
project.pledged -= msg.value;
pledgedMoney[_id][msg.sender] -= msg.value;
payable(msg.sender).transfer(msg.value);
}
6.增加提取余额用户
添加可以提取合约余额的账户列表功能
定义一个函数来增加提取余额的账户,当发起众筹的用户想要添加可以提取的账户地址,同时也会有一定的限制,调用改函数的账户一定要是该项目的创建者,同时众筹的余额要满足项目需要的金额,将该新的账户地址标记为false
// 添加一个新的账户到合约中
function addAccount(uint256 _id, address _newAccount) public {
Project storage project = projects[_id];
require(project.creator == msg.sender, "not creator");
require(project.pledged >= project.goalMoney* 1e18, "pledge < goalMoney");
require(!project.extracted, "claimed");
accounts.push(_newAccount);
project.extracted = false; // 重置已领取状态为false
}
7.取消用户提取余额权限
当项目的创建者想要取消一些账户的提取余额权限,就可以取消该地址提取余额的权利,在提取余额账户列表的数组中遍历得到要取消权限的地址,如果找到,将该地址移除合法权限列表
//移除提取余额的用户
function removeAccount(uint256 _id, address _accountToRemove)
public
{
Project storage project = projects[_id];
require(project.creator == msg.sender, "not authorized");
uint256 indexToRemove = 0;
for (uint256 i = 0; i < accounts.length; i++) {
if (accounts[i] == _accountToRemove) {
indexToRemove = i;
break;
}
}
// 从数组中移除用户遍历
for (uint256 i = indexToRemove; i < accounts.length - 1; i++) {
accounts[i] = accounts[i + 1];
}
accounts.pop();
}
8.在Matemask上进行部署合约,同时要使用自己的私链
9.效果图前端页面
10.项目的创建
11.投资人捐款
12.捐款成功可以获得一个代币的奖励