Dapp 众筹项目1 合约代码编写

我们为什么要写一个众筹Dapp?

      区块链的公开,透明,不可篡改,可追溯的特点。可保障投资者的利用,让项目方踏实做事

      <最想解决的就是投资者不管是投资项目,还是捐款,都能大部分的掌控自己的钱以及知道自己的钱花在哪里,刚好,可以利用区块链的,公开,透明,可追溯,不可篡改的特点,来完成我们的目的>

        2017年以太坊创始人V神提出了一个非常有创意的DAICO模型

我们可以用一个简单的案例来了解一下:

项目方:发起希望图书室建设----投资人进行投资并获得相应的代币---当项目发要花投资者投的这笔钱时,需要进行请求---

单一花费单一请求----当投资者同意数过半--智能合约自动把这笔钱给项目方。

全过程公开透明,钱用在何处,转给谁都公开透明,可追溯,减少作假可能。

基于以上思想,我们开发了一个基于以太坊智能合约的Dapp应用

                   1.项目方能发起众筹

                    2.项目方能提出花费请求

                    3.投资者能参与众筹

                    4.投资者能对参与众筹的项目进行资金支出的投票

                    5.投资者和项目方均能看见花费的详细信息

一.功能分析

     (1)角色

                    项目方:发起众筹

                    投资方:参与众筹

     (2)功能

                   项目方发起众筹,指定时间内众筹未达目标,则退款。

                  项目方能发起花费请求,花费请求必须通过参与者的投票票数决定是否执行,超过一半既可以执行。

                  投资方一个花费请求只能投一次票

                   投资方和项目方都能查看项目的完整信息。

二.合约编写

单个合约funding.sol编写

        (1).一个项目需包含的信息:项目方,项目名,项目目标筹集金额,平均支持金额,项目结束时间,参与者

//1.项目发起人
    //2.项目名称
    //3.项目目标筹集金额
    //4.每个人支持多少钱
    //5.项目持续多少天
    address public manager;
    string public projectName;
    uint256 public targetMoney;
    uint256 public supportMoney;
    uint256 public endTime;//终结时间
    address[]  investors;//所有参与方数组
    SupportorFundingContract supportorFundings;//参与这个项目的人员
    //构造函数
    constructor(string _projectName,uint256 _targetMoney,uint256 _supportMoney,uint256 _duration,address _creator,SupportorFundingContract _supportorFundings) public{
        manager = _creator;
        projectName = _projectName;
        targetMoney =_targetMoney;
        supportMoney=_supportMoney;
        endTime =block.timestamp +_duration;//block.timestamp当前时间
        supportorFundings = _supportorFundings;
    }

          (2).单一项目需具有参与众筹(invest),查看筹集金额(getBalance),查看所有参与者(getInvestors),退款(refund)等基本方法

               

  //使用一个mapping来判断一个地址是否是投资人,这样可以快速识别是否有投票权
    mapping(address=>bool)isInvestorMap;
    //投资 如果等于支付金额,则写入参与人列表
    function invest() payable public{
        require(msg.value == supportMoney);
        investors.push(msg.sender);
        isInvestorMap[msg.sender]=true;
        //将投资人与当前合约的地址传递到FundingFactory中
        //supportorFundings[msg.sender].push(this);
        supportorFundings.setFunding(msg.sender,this);
    }
    //退款,由前端调用
    function refund() onlyManager public {

        for (uint256 i=0;i<investors.length;i++){
            investors[i].transfer(supportMoney);
        }
        delete investors;
    }
    //查看当前余额
    function getBalance() public view returns(uint256){
        return address(this).balance;
    }
    //查看当前参与人
    function getInvestors() public view returns(address[]){
        return investors;
    }

                (3).项目方需进行花费请求,所以我们需要定义一个花费结构体,它应该包含(用途(purpose):买什么,金额(cost):需要花费多少,商家地址(seller):向谁购买,赞成票数(approveCount):多少参与方赞成(因为只有超过半数才能花费),花费申请状态(status):完成(已经花费),带批准(超过半数赞成),待执行(发起花费请求), 标记投过票的参与者(isVotedMap):防止重复投票)

 //产品状态枚举:0:进行中,1:已批准,2:已完成
    enum RequstStatus{
        Voting,Approved,Completed
    }
    //花费请求结构
    struct Request {
        string purpose;//用途
        uint256 cost;//花费金额
        address seller;//商家地址
        uint256 approveCount;//赞成票数
        RequstStatus status;
        //记录投资人对这个请求的投票状态,只有未投票的才能投票,每人一票
        mapping(address =>bool) isVotedMap;
    }

                 (4).一个项目可发起多个花费申请。定义一个数组(allRequests)记录所有发起的支付申请。定义发起申请的方法(createRequest)

 //请求可能有多个,定义一个请求数组
    Request[] public allRequests;
    function createRequest(string _purpose,uint256 _cost,address _seller)onlyManager public{
        Request memory req = Request({
            purpose:_purpose,
            cost:_cost,
            seller:_seller,
            approveCount:0,
            status:RequstStatus.Voting
        });
        allRequests.push(req);//将请求放入请求数组里
    }

                 (5).项目方发起花费申请后,需参与者进行投票,然后实现花费申请是否执行,为此我们需检查参与者是否已经投过票。定义了一个支持花费申请的函数(approveRequest)

 function approveRequest(uint256 i) public {
        //识别投资人是否投过票
        require(isInvestorMap[msg.sender]);
        //一定要使用storage类型,引用类型,否则无法修改allRequests里面的数据
        Request storage req=allRequests[i];
        require(req.isVotedMap[msg.sender]==false);
        req.approveCount++;
        req.isVotedMap[msg.sender]=true;
    }

                  (6).当参与者完成投票后,我们就要检查花费请求是否通过,通过条件:赞成票数过半,账户余额大于花费申请金额

定义了一个完成花费请求的函数(finalizeRequest),点击完成,即完成交易和转账。

function finalizeRequest(uint256 i) onlyManager public{
        Request storage req=allRequests[i];
        //金额足够
        require(address(this).balance >= req.cost);
        //票数过半
        require(req.approveCount * 2 >investors.length);
        //执行转账
        req.seller.transfer(req.cost);
        //	更新request状态
        req.status =RequstStatus.Completed;
    }

             (7).退款(refund),花费申请表(createRequest),完成花费(finalizeRequest)都应该是发起方才能进行的,所有我们需给这三个函数增加权限控制。定义一个修饰器,然后在方法上定义修饰器

 //权限控制
    modifier onlyManager{
        require(msg.sender==manager);
        _;
    }

               (8).我们还需要创建几个辅助函数,如,查看众筹项目剩余时间(getLeftTime),投资人数有多少(getInvestorsCount),

花费申请数量(getRequestsCount),某花费申请的具体信息(getRequestByIndex)

  //众筹剩余时间 返回秒
    function getLeftTime() public view returns(uint256){
        return (endTime-block.timestamp);
    }
    //投资人数
    function getInvestorsCount() public view returns(uint256){
        return investors.length;
    }
    //请求数量
    function getRequestsCount() public view returns(uint256) {
        return allRequests.length;
    }
    //某花费申请具体信息
    function getRequestByIndex(uint256 i) public view returns(string,uint256,address,uint256,RequstStatus){
        Request memory req=allRequests[i];
        return (req.purpose,req.cost,req.seller,req.approveCount,req.status);
    }

合约工厂 FundingFactory.sol编写

       每个人都可以创建单个众筹合约,一个人也可创建多个众筹合约,为了方便管理这些合约实例,便设置了合约工厂模式,方便管理合约。

       (1).工厂合约因包含平台管理员(platformManager),平台所有的众筹合约(allFundings),自己创建合约集合(reatorFundings),自己参与过的合约集合(supportorFundings)

 // 0.平台管理员
    address public platformManager;
    //1.所有众筹合约集合
    address [] allFundings;
    //2.创建人的合约集合
    mapping(address => address[]) creatorFundings;
    //3.参与人的合约集合
    // mapping(address => address[]) supportorFundings;
    SupportorFundingContract supportorFundings;
    //构造函数 
    constructor() public{
        platformManager =msg.sender;
        //构造函数时创建一个全局的合约实例
        supportorFundings= new SupportorFundingContract();
    }

         (2).合约工厂是用来管理和创建合约的,所以我们需定义创建合约的方法(createFunding)

function createFunding(string _name,uint _targetMoney,uint _supportMoney,uint _duration)public{
       //创建一个合约,使用new方法,同时传入参数,返回一个地址
       address funding= new Funding(_name,_targetMoney,_supportMoney,_duration,msg.sender,supportorFundings);
       allFundings.push(funding);
       //维护创建者所创建的合约集合
       creatorFundings[msg.sender].push(funding);
    }

        (3).由于我们要将这些信息返回到平台上,定义几个辅助函数,返回平台所有合约(getAllFundings),返回创建者所有合约(getCreatorFundings),获取参与者所有合约(getSupportorFunding)

 //返回平台所有合约
    function getAllFundings() public view returns(address[]){
        return allFundings;
    }
    //返回创建者所有合约
    function getCreatorFundings() public view returns(address[]){
        return creatorFundings[msg.sender];
    }
    //获取参与合约集合
    function getSupportorFunding() public view returns(address[]){
        return supportorFundings.getFundings(msg.sender);
    }

由于solidity不支持在函数参数中定义复杂类型,所以我们无法直接把(支持合约的参与者集合)supportFundings变量传给invest方法。这里我们创建一个临时合约,该合约维护了所有参与者参与过的众筹合约。

SupportorFundingContract.sol

//这个合约维护全局所有参与人所参与的合约
contract SupportorFundingContract{
    mapping(address=>address[])supportorFundingsMap;
    function setFunding(address _supptor,address _funding){
        supportorFundingsMap[_supptor].push(_funding);
        
    }
    function getFundings(address _supptor)public view returns(address[]){
        return supportorFundingsMap[_supptor];
    }
}

到这里,众筹的智能合约已经开发完毕了!!

然后让我们看看结果吧:

1.打开ganache-clli(模拟区块链):node_modules/.bin/ganache-cli

2.启动项目:npm run start  (react开发的启动命令)

3.在浏览器中访问 http://localhost:3000/    主界面

4.在metaMask钱包中添加账户

在ganache-cli中获取私钥:

在metamesk中添加

5.初始创世块的创建,需在代码中添加合约地址

6.现在网页就可以查看当前账户的地址了,与钱包里的地址一致(下面我们直接叫他account3(0x1b))

目前没有一个合约!!

7.使用account3作为项目方创建俩个众筹项目--刷新--合约显示

8.accounts3对《希望图书室建设》进行投资:--刷新

9.accounts2对《希望图书室建设》进行投资:在钱包切换账号--刷新--投票后,在我参与的界面可查询

10.account3在我发起的界面对“希望图书室建设”进行花费请求

11.创建花费请求成功后可在申请详情里查看

12.切换acount2账户进行投票支持

13.切换account3进行花费申请完成交易

猜你喜欢

转载自blog.csdn.net/qq_38798147/article/details/109308787