// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
//此时可以使用 import "./interface/IERC20.sol";但由于SimpleCrowdsale 只使用到了如下3个方法,而定义之
interface Itoken{
function decimals() external view returns (uint8);
function transfer(address recipient, uint amount) external returns (bool);
function balanceOf(address account) external view returns (uint);
}
/**
* 众筹合约
*/
contract SimpleCrowdsale {
//受益人地址, 构造函数传入的是合约的创建者,但也可以指定不同的受益地址
address public immutable beneficiary;
//发行代币的地址
address public token;
//发行代币的地址精度
uint8 public decimals;
//众筹目标,单位是ether
uint public fundingGoal;
//已筹集金额数量, 单位是ether
uint public amountRaised;
//截止时间
uint public deadline;
//代币价格
uint public price;
//是否达成众筹目标
bool public fundingGoalReached = false;
//众筹关闭
bool public crowdsaleClosed = false;
//保存众筹地址及对应的以太币数量
mapping(address => uint256) public balance;
//保存所有众筹地址
address[] public fundingList;
// 受益人将众筹金额转走的通知
event GoalReached(address _beneficiary, uint _amountRaised);
// 用来记录众筹资金变动的通知,_isContribution表示是否是捐赠,因为有可能是捐赠者退出或发起者转移众筹资金
event FundTransfer(address _backer, uint _amount, bool _isContribution);
/**
* @param fundingGoalInEthers 众筹以太币总量
* @param durationInDays 众筹截止,单位是天
* @param _token 奖励代币地址
*/
constructor(uint fundingGoalInEthers,uint durationInDays,address _token) {
beneficiary=msg.sender;
fundingGoal = fundingGoalInEthers * 1 ether;
deadline = block.timestamp + durationInDays * 1 days; //最后换算出来是秒
price = 0.5 ether;
token=_token;
decimals=Itoken(_token).decimals();
}
/**
* 默认函数,可以向合约直接打款
*/
receive() external payable
{
buyToken();
}
//购买代币
function buyToken() public payable
{
//判断是否关闭众筹
require(!crowdsaleClosed,"Crowdsale closeed");
uint amount = msg.value;
//转入amount 个ETH 根据price能买入多少个 ERC20 代币
uint tokenBuyAmount=amount / price * 10 ** uint256(decimals);
Itoken _t=Itoken(token);
//合约账号代币余额不足
require(_t.balanceOf(address(this))>=tokenBuyAmount,"Crowsale contract token amount insufficient");
require(_t.transfer(msg.sender,tokenBuyAmount),"Token transfer failed");
//捐款人的金额累加
balance[msg.sender] += amount;
//捐款总额累加
amountRaised += amount;
fundingList.push(msg.sender);
//转帐操作,转多少代币给捐款人
emit FundTransfer(msg.sender, amount, true);
}
/**
* 判断是否已经过了众筹截止限期
*/
modifier afterDeadline() { if (block.timestamp >= deadline) _; }
/**
* 时间到期后,检测众筹目标是否已经达到,
*/
function checkGoalReached() afterDeadline public {
if (amountRaised >= fundingGoal){
//达成众筹目标
fundingGoalReached = true;
emit GoalReached(beneficiary, amountRaised);
}
//关闭众筹
crowdsaleClosed = true;
}
function getBalance() public view returns(uint)
{
return payable(this).balance;
}
/**
* 资金去留处理
* 检查是否达到了目标或时间限制,如果有,并且达到了资金目标,
* 将全部金额发送给受益人。如果没有达到目标,每个贡献者都可以退出并撤资
* 注:这里代码应该是限制了众筹时间结束且众筹目标没有达成的情况下才允许退出。如果去掉限制条件afterDeadline,应该是可以允许众筹时间还未到且众筹目标没有达成的情况下退出
*/
function safeWithdrawal() public payable afterDeadline {
//如果没有达成众筹目标
if (!fundingGoalReached) {
//获取合约调用者已捐款余额
uint amount = balance[msg.sender];
if (amount > 0) {
//返回合约发起者所有余额
payable(msg.sender).transfer(amount);
emit FundTransfer(msg.sender, amount, false);
balance[msg.sender] = 0;
}
}
//如果达成众筹目标,并且合约调用者是受益人
if (fundingGoalReached && beneficiary == msg.sender) {
//将所有捐款从合约中给受益人
payable(beneficiary).transfer(amountRaised);
emit FundTransfer(beneficiary,balance[msg.sender],false);
}
}
}
bin与abi:
abi:Application Binary Interface,应用二进制接口文件。合约说明
智能合约的接口描述,描述了字段名称、字段类型、方法名称、参数名称、参数类型、方法返回值类型等。
当合约被编译后,对应的abi文件也就确定了。流程是先编译成evm可以识别的bytecode(bin文件),同时生成abi文件,然后进行部署
如一个abi文件内容:
[{"inputs":[],"name":"a","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]
对应的bin文件内容:
6000805461ff00191661010017905560c0604052600360809081526235363760e81b60a052600190610031908261010b565b5060408051808201909152600381526235363760e81b6020820152600290610059908261010b565b5034801561006657600080fd5b506101ca565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061009657607f821691505b6020821081036100b657634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561010657600081815260208120601f850160051c810160208610156100e35750805b601f850160051c820191505b81811015610102578281556001016100ef565b5050505b505050565b81516001600160401b038111156101245761012461006c565b610138816101328454610082565b846100bc565b602080601f83116001811461016d57600084156101555750858301515b600019600386901b1c1916600185901b178555610102565b600085815260208120601f198616915b8281101561019c5788860151825594840194600190910190840161017d565b50858210156101ba5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61018c806101d96000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80630dbe671f1461003b57806312a7b9141461005c575b600080fd5b6000546100489060ff1681565b604051901515815260200160405180910390f35b6100486000600260405160200161007391906100b7565b60405160208183030381529060405280519060200120600160405160200161009b91906100b7565b6040516020818303038152906040528051906020012014905090565b600080835481600182811c9150808316806100d357607f831692505b602080841082036100f257634e487b7160e01b86526022600452602486fd5b818015610106576001811461011b57610148565b60ff1986168952841515850289019650610148565b60008a81526020902060005b868110156101405781548b820152908501908301610127565b505084890196505b50949897505050505050505056fea2646970667358221220e0c6a43c71fe85bb31a72ffd77f8017c78dbbfd195073ed61a240085544c000564736f6c63430008110033