以太坊 day (6) 简单众筹项目的演示

以太坊 day (6)

  • 一、众筹项目
    • 1.1 众筹规则
  • 二、众筹合约实现
    • 2.1 字段
    • 2.2 添加投资函数
    • 2.3 发起人请求花费的结构体定义
    • 2.4 众筹失败,原路退回资金
    • 2.5 甲方创建花费请求
    • 2.6 投资人对花费请求进行投票
    • 2.7 执行花费动作
    • 2.8 辅助函数
    • 2.9 怎么快速检验是不是投资人
    • 2.10 Funding合约完整代码
  • 三、工厂合约
    • 3.1工厂合约包含的内容
    • 3.2 管理者创建众筹平台构造函数
    • 3.3 用户创建众筹项目构造函数
    • 3.4 辅助函数
  • 四、supportFunding中间合约
    • 4.1 supportFunding中间合约
    • 4.2 mapping无法通过函数传递
    • 4.3 FactoryFunding引入中间合约变量supportFunding
    • 4.4 Funding合约接收supportFunding
  • 五、收获
    • 5.1 合约间不能传递复杂的mapping
    • 5.2 Funding合约中的构造函数中的msg.sender
    • 5.3 msg.sender,msg.value,this
    • 六、代码
    • 6.1 最终版Funnding代码
    • 6.2 最终版FactoryFundingContract合约代码
    • 6.3 最终版supportFunding代码
    • 6.4 项目运行步骤

一、众筹项目

1.1 众筹规则

  • 所有用户都可以参与众筹

  • 规定时间内筹得资金则成功,反之失败

  • 众筹的钱存放在合约里,发起众筹的人想花费合约中的钱的时候需要提交申请,投资者投票过半,申请才可以通过
    -投资者对申请只能投一次票

  • 花费申请成功时,可以获得花费的权力

二、众筹合约实现

2.1 字段

  • 项目发起人(address public launch)
  • 需要资金
  • 人均投资
  • 持续时间
  • 项目名称
pragma solidity ^0.4.26;
contract Funding{
    string public pro_name;
    uint256 public endtime;
    uint256 public need_money;
    uint256 public peo_money;
    address  public launch;
    constructor(string  pro_name1,uint256 endtime1,uint256 need_money1,uint256 peo_money1) public {
        launch = msg.sender;//谁调用这个合约谁就是项目发起人
        pro_name = pro_name1;
        endtime = block.timestamp + endtime1;
        peo_money = peo_money1;
        need_money = need_money1;
    }
    function getName() public view returns (string){
        return pro_name;
    }
}

2.2 添加投资函数

   function invest() public  payable{
        require(msg.value == 1000);
        investors.push(msg.sender);
    }
    function getContractBalance() public view returns (uint256){
        return address(this).balance;
    }
    function getInvestors() public view returns  (address[]){
        return investors;
    }

2.3 发起人请求花费的结构体定义

    enum AppliStatue{
        //正在进行中,已赞成,已结束
        voting, approved, finish
    }
    struct Application{
        //花费的请求
        string spendReason;
        //商家的地址
        address seller;
        //请求的状态
        appliStatue statue;
        //赞成的数量
        uint256 approve;
        //请求花费多少
        uint256 cost;
        //每个投资者的投资状况
        mapping(address=>bool) isVotedMap;
    }

2.4 众筹失败,原路退回资金

   function refund() public {
        require(msg.sender == launch);
        for (uint256 i = 0;i < investors.length; i ++){
            investors[i].transfer(supportMoney);
        }
        delete investors;
    } 

2.5 甲方创建花费请求

  //创建花费请求
    function createEat(string _spendReason, address _seller,uint256 _cost ) public {
        Application memory myRequest = Application({
            spendReason : _spendReason,
            seller : _seller,
            statue : AppliStatue.voting,
            approve : 0,
            cost :_cost
        });
        allApplication.push(myRequest);
    }

2.6 投资人对花费请求进行投票

    //投资人对花费请求进行批准
    function voteRequest(uint256 i) public{
        //判断投资人是否据有投资资格
        require(isVoter[msg.sender] == true);
        //这里一定是storage 类型
        Application storage app = allApplication[i];
        //判断是否已经投过票
        require(app.isVotedMap[msg.sender] == false);
        app.approve = app.approve+ 1;
        app.isVotedMap[msg.sender] == true;
    }
    

2.7 执行花费动作

  • 判断投票是否过半
  • 判断合约的钱是否大于请求的钱
  • 将花费的钱转给商家
  • 更新花费申请的状态
  //执行花费动作
    function startSpend(uint256 i) public{
        Application storage app = allApplication[i];
        //判断投票是否过半
        require(app.approve * 2 > investors.length);
        //判断合约的钱是否大于请求的钱
        require(address(this).balance > app.cost);
        //将花费的钱转给商家
        app.seller.transfer(app.cost);
        //更新花费申请的状态
        app.statue = AppliStatue.finish;
    }

2.8 辅助函数

  • 权限控制
  • 返回投资人数
  • 返回申请请求详细信息
  • 返回众筹剩余时间
   //权限控制
    modifier onlyManager(){
        //只有投资者才拥有的权限
        require(msg.sender == launch);
        _;
    }
    //获取投资人的数量
    function getInvestNum() public view returns (uint256){
        return investors.length;
    }
    //返回众筹剩余时间
    function leftTime() public view returns (uint256){
        return endtime - block.timestamp;
    }
    //申请请求详细信息,可以返回自定义的枚举类型
    function detailApplication(uint256 i) public view returns (string, address, uint256, uint256, AppliStatue){
        Application storage app = allApplication[i];
        return (app.spendReason, app.seller, app.cost, app.approve, app.statue);
    }

2.9 怎么快速检验是不是投资人

mapping (address=>bool) isVoter;

2.10 Funding合约完整代码

  • 版本1.0
pragma solidity ^0.4.26;
contract Funding{
    string public pro_name;
    uint256 public endtime;
    uint256 public need_money;
    uint256 public peo_money;
    address  public launch;
    mapping (address=>bool) isVoter;
    address[] investors;
    uint256 supportMoney;
    constructor(string  pro_name1,uint256 endtime1,uint256 need_money1,uint256 peo_money1) public {
        launch = msg.sender;
        pro_name = pro_name1;
        endtime = block.timestamp + endtime1;
        peo_money = peo_money1;
        need_money = need_money1;
        supportMoney = peo_money;
    }
     enum AppliStatue{
        //正在进行中,已赞成,已结束
        voting, approved, finish
    }
    struct Application{
        //花费的请求
        string spendReason;
        //商家的地址
        address seller;
        //请求的状态
        AppliStatue statue;
        //赞成的数量
        uint256 approve;
        //请求花费多少
        uint256 cost;
        //每个投资者的投资状况
        mapping(address=>bool) isVotedMap;
    }
    Application[] allApplication;
    //众筹失败,返回钱,只能项目发起者退回
    function invest() public  payable{
        require(msg.value == 1000);
        investors.push(msg.sender);
    }

    function getName() public view returns (string){
        return pro_name;
    }

    function getContracbalance() public view returns(uint256){
          return address(this).balance;
    }
    function getInvestors() public view returns  (address[]){
        return investors;
    }
    function createEat(string _spendReason, address _seller,uint256 _cost ) public {
        Application memory myRequest = Application({
            spendReason : _spendReason,
            seller : _seller,
            statue : AppliStatue.voting,
            approve : 0,
            cost :_cost
        });
        allApplication.push(myRequest);
    }
    //投资人对花费请求进行批准
    function voteRequest(uint256 i) public{
        //判断投资人是否据有投资资格
        require(isVoter[msg.sender] == true);
        //这里一定是storage 类型
        Application storage app = allApplication[i];
        //判断是否已经投过票
        require(app.isVotedMap[msg.sender] == false);
        app.approve = app.approve+ 1;
        app.isVotedMap[msg.sender] == true;
    }
     function startSpend(uint256 i) public{
        Application storage app = allApplication[i];
        //判断投票是否过半
        require(app.approve * 2 > investors.length);
        //判断合约的钱是否大于请求的钱
        require(address(this).balance > app.cost);
        //将花费的钱转给商家
        app.seller.transfer(app.cost);
        //更新花费申请的状态
        app.statue = AppliStatue.finish;
    }
      modifier onlyManager(){
        //只有投资者才拥有的权限
        require(msg.sender == launch);
        _;
    }
    function getInvestNum() public view returns (uint256){
        return investors.length;
    }
    function leftTime() public view returns (uint256){
        return endtime - block.timestamp;
    }

    //可以返回自定义的枚举类型
    function detailApplication(uint256 i) public view returns (string, address, uint256, uint256, AppliStatue){
        Application storage app = allApplication[i];
        return (app.spendReason, app.seller, app.cost, app.approve, app.statue);
    }
    function refund() public{
        require(msg.sender == launch);
        for (uint256 i = 0;i < investors.length; i++){
           investors[i].transfer(supportMoney);
        }
        delete investors;
    } 

}

三、工厂合约

3.1工厂合约包含的内容

  • 所有的合约
    address[]
  • 我参加的众筹合约
    mapping(address=>address[])
  • 我参与的众筹合约
    mapping(address=>address[])
  • 平台管理员
    //众筹项目工厂管理者
    address public FactoryManager;
    //所有的众筹项目
    address [] public allCrowdFunding;
    //用户创建的众筹项目
    mapping(address=>address[]) ownCrowdFunding;
    //用户参与的众筹项目
    mapping(address=>address[]) joinCrowdFunding;
    //中间合约,负责用户参与的所有众筹项目
    SupportFunding public supportFunnding;

3.2 管理者创建众筹平台构造函数

  constructor() public{
        FactoryManager = msg.sender;
        //supportFunnding相当于维护了一个去全局变量
        supportFunnding = new SupportFunding();
    }

3.3 用户创建众筹项目构造函数

 //创建众筹项目
      function createCrowdFunding(string  pro_name1,uint256 endtime1,uint256 need_money1,uint256 supportMoney1, SupportFunding supportFunding) public {
        address funding  = new Funding(pro_name1,endtime1,need_money1,supportMoney1,supportFunding);
        allCrowdFunding.push(funding);
        ownCrowdFunding[msg.sender].push(funding);
        
    }

3.4 辅助函数

    //得到所有的众筹项目的地址
    function getAllFunding() public view returns(address[]){
        return allCrowdFunding;
    }
    //得到一个用户发起的所有众筹项目
    function getAllCreatefunding() public view returns(address[]){
        return ownCrowdFunding[msg.sender];
    }
    //得到用户参与的所有的众筹项目
      function getAllJoinFunding() public view returns (address[]){
        return supportFunnding.getJoinFunding(msg.sender);
    }

四、supportFunding中间合约

4.1 supportFunding中间合约

  • 负责维护用户参与众筹的mapping
pragma solidity ^0.4.26;
contract SupportFunding{
    mapping(address=>address[]) public joinFunding;
    //获取用户参与的所有众筹项目
    function getJoinFunding(address _user) public view returns (address[]){
        return joinFunding[_user];
    }
    //将用户加入的众筹项目
    function setJoinFunding(address _user, address _fund) public {
        joinFunding[_user].push(_fund);
    }
}

4.2 mapping无法通过函数传递

  • 问题描述
    Funding合约中的投资人需要加入到我参与的所有合约的mapping中,而我参与的所有合约的mapping在FactoryFunding合约中,而FactoryFunding合约不能直接将JoinMapping通过参数传递到Funding合约
  • 解决方法虽然不能传递复杂的mapping,但是可以传递一个合约

4.3 FactoryFunding引入中间合约变量supportFunding

  • SupportFunding
   //创建众筹项目
      function createCrowdFunding(string  pro_name1,uint256 endtime1,uint256 need_money1,uint256 supportMoney1, SupportFunding supportFunding) public {
        address funding  = new Funding(pro_name1,endtime1,need_money1,supportMoney1,supportFunding);
        allCrowdFunding.push(funding);
        ownCrowdFunding[msg.sender].push(funding);
        
    }

4.4 Funding合约接收supportFunding

  • 构造函数接受传参_supportFunnding
 constructor(string  pro_name1,uint256 endtime1,uint256 need_money1,uint256 peo_money1,SupportFunding _supportFunnding) public {
        launch = msg.sender;
        pro_name = pro_name1;
        endtime = block.timestamp + endtime1;
        peo_money = peo_money1;
        need_money = need_money1;
        supportMoney = peo_money;
        supportFunnding = _supportFunnding;
    }

五、收获

5.1 合约间不能传递复杂的mapping

  • 可以使用中间合约作为全局变量来解

5.2 Funding合约中的构造函数中的msg.sender

  • 问题描述用户User1在合约工厂中调用创建合约函数A,Funding合约中构造函数A中的msg.sender是此时合约工厂的创建者,不是User1,会出错
  • 解决方法:Funding中的用户建议使用工厂合约中的地址传入

5.3 msg.sender,msg.value,this

  • this是当前用户所在合约的地址
  • msg.sender是当前函数调用者的账户地址
  • msg.value是当前用户准备投资的钱

六、代码

6.1 最终版Funnding代码

pragma solidity ^0.4.26;
import './SupportFunding.sol';
contract Funding{
    SupportFunding supportFunnding;
    string public pro_name;
    uint256 public endtime;
    uint256 public need_money;
    uint256 public peo_money;
    address  public launch;
    mapping (address=>bool) isVoter;
    address[] investors;
    uint256 supportMoney;
    constructor(string  pro_name1,uint256 endtime1,uint256 need_money1,uint256 peo_money1,SupportFunding _supportFunnding) public {
        launch = msg.sender;
        pro_name = pro_name1;
        endtime = block.timestamp + endtime1;
        peo_money = peo_money1;
        need_money = need_money1;
        supportMoney = peo_money;
        supportFunnding = _supportFunnding;
    }
     enum AppliStatue{
        //正在进行中,已赞成,已结束
        voting, approved, finish
    }
    struct Application{
        //花费的请求
        string spendReason;
        //商家的地址
        address seller;
        //请求的状态
        AppliStatue statue;
        //赞成的数量
        uint256 approve;
        //请求花费多少
        uint256 cost;
        //每个投资者的投资状况
        mapping(address=>bool) isVotedMap;
    }
    Application[] allApplication;
    //众筹失败,返回钱,只能项目发起者退回
    function invest() public  payable{
        require(msg.value == 1000);
        investors.push(msg.sender);
        isVoter[msg.sender] = true;
        //this表示当前合约
        supportFunnding.setJoinFunding(msg.sender, this);
    }

    function getName() public view returns (string){
        return pro_name;
    }

    function getContracbalance() public view returns(uint256){
          return address(this).balance;
    }
    function getInvestors() public view returns  (address[]){
        return investors;
    }
    function createEat(string _spendReason, address _seller,uint256 _cost ) public {
        Application memory myRequest = Application({
            spendReason : _spendReason,
            seller : _seller,
            statue : AppliStatue.voting,
            approve : 0,
            cost :_cost
        });
        allApplication.push(myRequest);
    }
    //投资人对花费请求进行批准
    function voteRequest(uint256 i) public{
        //判断投资人是否据有投资资格
        require(isVoter[msg.sender] == true);
        //这里一定是storage 类型
        Application storage app = allApplication[i];
        //判断是否已经投过票
        require(app.isVotedMap[msg.sender] == false);
        app.approve = app.approve+ 1;
        app.isVotedMap[msg.sender] == true;
    }
     function startSpend(uint256 i) public{
        Application storage app = allApplication[i];
        //判断投票是否过半
        require(app.approve * 2 > investors.length);
        //判断合约的钱是否大于请求的钱
        require(address(this).balance > app.cost);
        //将花费的钱转给商家
        app.seller.transfer(app.cost);
        //更新花费申请的状态
        app.statue = AppliStatue.finish;
    }
      modifier onlyManager(){
        //只有投资者才拥有的权限
        require(msg.sender == launch);
        _;
    }
    function getInvestNum() public view returns (uint256){
        return investors.length;
    }
    function leftTime() public view returns (uint256){
        return endtime - block.timestamp;
    }

    //可以返回自定义的枚举类型
    function detailApplication(uint256 i) public view returns (string, address, uint256, uint256, AppliStatue){
        Application storage app = allApplication[i];
        return (app.spendReason, app.seller, app.cost, app.approve, app.statue);
    }
    function refund() public{
        require(msg.sender == launch);
        for (uint256 i = 0;i < investors.length; i++){
           investors[i].transfer(supportMoney);
        }
        delete investors;
    }
}

6.2 最终版FactoryFundingContract合约代码

pragma solidity ^0.4.26;
import './Fundding.sol';
import './SupportFunding.sol';
contract FactoryFundingContract{
    
    //众筹项目工厂管理者
    address public FactoryManager;
    //所有的众筹项目
    address [] public allCrowdFunding;
    //用户创建的众筹项目
    mapping(address=>address[]) ownCrowdFunding;
    //用户参与的众筹项目
    mapping(address=>address[]) joinCrowdFunding;
    SupportFunding supportFunnding;
    constructor() public{
        FactoryManager = msg.sender;
        //supportFunnding相当于维护了一个去全局变量
        supportFunnding = new SupportFunding();
    }
    //创建众筹项目
      function createCrowdFunding(string  pro_name1,uint256 endtime1,uint256 need_money1,uint256 supportMoney1, SupportFunding supportFunding) public {
        address funding  = new Funding(pro_name1,endtime1,need_money1,supportMoney1,supportFunding);
        allCrowdFunding.push(funding);
        ownCrowdFunding[msg.sender].push(funding);
        
    }
      function getAllFunding() public view returns(address[]){
        return allCrowdFunding;
    }
    function getAllCreatefunding() public view returns(address[]){
        return ownCrowdFunding[msg.sender];
    }
     function getAllJoinFunding() public view returns (address[]){
        return supportFunnding.getJoinFunding(msg.sender);
    }
}

6.3 最终版supportFunding代码

pragma solidity ^0.4.26;
contract SupportFunding{
    mapping(address=>address[]) public joinFunding;
    function getJoinFunding(address _user) public view returns (address[]){
        return joinFunding[_user];
    }
    function setJoinFunding(address _user, address _fund) public {
        joinFunding[_user].push(_fund);
    }
}

6.4 项目运行步骤

  • 用户1创建整个众筹项目,为总项目管理者
    用户1部署FactoryFundding.sol
  • 用户2创建一个众筹项目,得到众筹项目的地址
    用户2执行创建众筹项目的方法,得到一个新的众筹项目的地址A
  • 部署上述得到的项目的地址A
    现在所有的用户都可以参与众筹项目

你可能感兴趣的:(以太坊,以太坊,区块链,比特币)