以太坊众筹项目合约以及部署02

以太坊众筹 Funding-React

JS-API 之 mocha测试框架

  • package.json mocha

    npm i -S mocha
    npm i mocha -g
    

    修改package.json

    {
        "scripts": {
       		"test": "mocha"
      	}
    }
    

  • bf/bfe/af/afe

    let person;
    // 在所有测试用例之前只执行一次
    before(() => {
        console.log("创建Person对象");
        person = new Person();
    });
    
    // 在每个测试用例之前都执行一次
    beforeEach(() => {
        console.log("创建Person对象");
        person = new Person();
    });
    
    after(function () {
        console.log("所有案例结束后执行一次after");
    });
    
    afterEach(function () {
        console.log("每个案例结束后执行一次afterEach");
    });
    

  • Error

    // mocha测试
    class Person {
        say() {
            return "hello";
        }
    
        eat() {
            try {
                console.log("吃吃吃");
                throw Error("噎住了");
            } catch (e) {
                // console.error(e);
            }
        }
    
        cal(a, b) {
            return a + b;
        }
    }
    

  • done (测试耗时/异步任务)

    let assert = require("assert");
    
    function add(a, b){
        return a + b;
    }
    describe('测试用例集合', function () {
        // 测试耗时/异步任务
        it('测试done', function (done) {
            setTimeout(() => {
                console.log("haha");
                assert.equal(add(1, 2), 888, "add方法测试失败");
                done();
            }, 1500)
        });
    });
    
  • 配置mocha测试最大耗时 (超时时长, 默认2000毫秒)

    mocha -t 5000 test/done.test.js

  • Promise

    • 右键运行类型
    • node版本 (我的10.11.0)
    • 运行超时配置
    • await web3.eth.getAccounts() 获取角标0

JS-API 之 web3封装

合约编译部署流程

let Web3 = require("web3");
let ganache = require("ganache-cli");

// 变量解构
let {FundingFactory, Funding} = require("../compile");

let web3 = new Web3(ganache.provider());

let ffContract;
deploy = async () => {
    // 获取账号
    const accounts = await web3.eth.getAccounts();
    // console.log(accounts[0]);

    let estimateGas = await web3.eth.estimateGas({data: "0x" + FundingFactory.bytecode});
    console.log(`estimateGas: ${estimateGas}`);

    ffContract = await new web3.eth.Contract(JSON.parse(FundingFactory.interface))
        .deploy({data: FundingFactory.bytecode})
        .send({from: accounts[0], gas: estimateGas});
};

before( async () => {
    await deploy();
});

describe('测试众筹合约', () => {
    it('获取账号', async () => {
        const accounts = await web3.eth.getAccounts();
        console.log(`accounts[0]` + accounts[0]);
    });
    it('查看合约对象', () => {
        console.log(ffContract);
    })
});

合约初始化流程

  • 编译合约, 得到两个合约的ABI (interface), bytecode (用于部署)
  • 把合约部署到ganache网络(rinkeby, mainnet)
  • 调用FundingFactory/Funding合约的函数…

优化合约编译

// 编译合约
let path = require("path");
let fs = require("fs");
let solc = require("solc");

let filePath = path.join(__dirname, "contracts", "Funding.sol");
let cachePath = path.join(__dirname, "contracts", "Funding-cache.json");

let compile;
let isReadCache = true;
if(isReadCache){
    let str = fs.readFileSync(cachePath, "utf-8");
    console.log(str);
    compile = JSON.parse(str);
}else {
    let content = fs.readFileSync(filePath, "utf-8");
    // console.log(content);
    compile = solc.compile(content, 1);
    // console.log(compile);
    fs.writeFileSync(cachePath, JSON.stringify(compile));
}

module.exports = {
    FundingFactory: compile.contracts[':FundingFactory'],
    Funding: compile.contracts[':Funding'],
};

web3js封装函数

  • 路人

    • 查看所有众筹项目列表 2 @
    • 根据项目地址获取项目详情 (名称, 结束时间, 目标金额, 参与金额, 人数) 2.5
    • 参与众筹项目 3 @
  • 众筹创建者

    • 发起众筹 1 @
    • 查看自己创建的众筹项目列表 4 @
    • 发起付款请求 6 @
    • 完成付款操作 8 @
  • 众筹参与者

    • 查看自己参与的众筹项目列表 5 @
    • 查看指定众筹项目的付款请求列表 7 @
    • 审核批准付款请求 7.5 @
  • 获取当前账户地址 0 @

合约补充

contract FundingFactory {
    
    //存储所有已经部署的智能合约的地址 
    address[] public fundings;
    
    // 创建者=> 合约地址数组 
    mapping(address => address[]) creatorToFundings;
    
    function createFunding(string _projectName, uint _supportMoney, uint _goalMoney) public {
        address funding = new Funding(_projectName, _supportMoney, _goalMoney, msg.sender);
        fundings.push(funding);
        
        // 把创建者创建的合约地址保存到其数组中
        creatorToFundings[msg.sender].push(funding);
    }
    
    // 查看所有众筹项目列表
    function getFundings() public view returns(address[]){
        return fundings;
    }
    
    // 查看调用者创建的所有众筹项目地址
    function getCreatorFunding() public view returns(address[]){
        return creatorToFundings[msg.sender];
    }
}

React页面框架搭建

React集成JS-API

补充完整代码(优化后)

  • 合约的方法调用其他合约方法 (获取我(参与者)参与的所有项目) 顺序整理:
    • 存储参与者的众筹项目
      1. 创造PlayerToFundings合约, 用于保存每个用户的所有参与的项目
      2. 在FundingFactory的构造函数(constructor)中初始化这个合约p2f
      3. 众筹创建者发起众筹(FundingFactory.createFunding)时, 把这个合约的引用p2f传给每一个众筹项目
      4. 当路人参与众筹(Funding.support)时, 调用p2f的join函数, 保存这个参与者的众筹项目
    • 读取自己参与的所有众筹项目
      1. 参与者调用FundingFactory的getPlayerFundings函数
      2. 由于FundingFactory没有保存相关参与数据, 所以转调了p2f的getPlayerFundings
      3. p2f把其保存的playerToFundings[player]信息(指定参与者的参与的所有项目)返回给FundingFactory
      4. FundingFactory最终把调用者参与的所有address[]返回给调用者
pragma solidity ^0.4.25;

contract PlayerToFundings{
    // 参与者=> 合约地址数组 
    mapping(address => address[]) playerToFundings;
    
    function join(address player, address fundingAddress) public {
        playerToFundings[player].push(fundingAddress);
    }
    
    function getPlayerFundings(address player) public view returns(address[]){
        return playerToFundings[player];
    }
    
}

contract FundingFactory {
    
    //存储所有已经部署的智能合约的地址 
    address[] public fundings;
    
    // 创建者=> 合约地址数组 
    mapping(address => address[]) creatorToFundings;
    
    PlayerToFundings p2f;
    constructor() public{
        address p2fAddress = new PlayerToFundings();
        p2f = PlayerToFundings(p2fAddress);
    }
    
    function createFunding(string _projectName, uint _supportMoney, uint _goalMoney) public {
        address funding = new Funding(_projectName, _supportMoney, _goalMoney, msg.sender, p2f);
        fundings.push(funding);
        
        // 把创建者创建的合约地址保存到其数组中
        creatorToFundings[msg.sender].push(funding);
    }
    
    // 路人 查看所有众筹项目列表
    function getFundings() public view returns(address[]){
        return fundings;
    }
    
    // 创建者 查看调用者创建的所有众筹项目地址
    function getCreatorFundings() public view returns(address[]){
        return creatorToFundings[msg.sender];
    }
    
    // 参与者 
    function getPlayerFundings() public view returns(address[]){
        return p2f.getPlayerFundings(msg.sender);
    }
}

contract Funding {
    
    // 众筹成功标记
    bool public flag = false;
    // 众筹发起人地址(众筹发起人)
    address public manager;
    // 项目名称
    string public projectName;
    // 众筹参与人需要付的钱
    uint public supportMoney;
    // 目标募集的资金(endTime后,达不到目标则众筹失败)
    uint public goalMoney;
    // 默认众筹结束的时间,为众筹发起后的一个月
    uint public endTime;
    // 众筹参与人的数组
    address[] public players;
    mapping(address=>bool) playersMap;
    
    PlayerToFundings p2f;
    
    // 付款请求的数组
    Request[] public requests; 
    
    struct Request {
        // 描述这笔付款请求是干啥的
        string description;
        // 花多少钱, 钱要少于balance
        uint money;
        // 钱汇给谁. 真正的收钱方
        address shopAddress;
        // 代表当前付款请求已经处理完毕
        bool complete;
        // 已投票的用户地址
        mapping(address=>bool) votedMap;
        uint votedCount;
    } 
    
    //构造函数
    constructor(string _projectName, uint _supportMoney, uint _goalMoney, address sender, PlayerToFundings _p2f) public{
        manager = sender;
        projectName =_projectName;
        supportMoney = _supportMoney;
        goalMoney = _goalMoney;
        endTime = now + 4 weeks;
        p2f = _p2f;
    }

    // 我要支持(需要付钱)
    function support() public payable{
        require(msg.value == supportMoney);
        // 放进集合中
        players.push(msg.sender);
        playersMap[msg.sender] = true;
        p2f.join(msg.sender, address(this));
    }
    
    // 付款申请函数,由众筹发起人调用
    function createRequest(string _description, uint _money, address _shopAddress) public onlyManagerCanCall{
        // 余额大于等于付款请求 
        require(address(this).balance >= _money);
        
        Request memory request = Request({
            description: _description,
            money: _money,
            shopAddress: _shopAddress,
            complete: false,
            votedCount: 0
        });
        
        requests.push(request);
    }
    
    // 付款批准函数, 由众筹参与人调用
    function approveRequest(uint index) public {
        // 是否是众筹参与者
        require(playersMap[msg.sender]);
        
        // 防止一个人重复投票
        Request storage request = requests[index];
        require(!request.votedMap[msg.sender]);
        
        request.votedMap[msg.sender] = true;
        request.votedCount++;
    }
    // 众筹发起人调用, 可以调用完成付款
    function finalizeRequest(uint index) public onlyManagerCanCall{
        
        Request storage request = requests[index];
        require(!request.complete);
        // 钱够
        require(address(this).balance >= request.money);
        // 人数过半
        require(request.votedCount * 2 >= getPlayersCount());
        
        // 执行转账操作
        request.shopAddress.transfer(request.money);
        request.complete = true;
    }
    
    
    // 所有参与者
    function getPlayers() public view returns(address[]){
        return players;
    }
    // 所有参与者个数
    function getPlayersCount() public view returns(uint){
        return players.length;
    }
    // 获取合约的余额
    function getTotalBalance() public view returns(uint){
        return address(this).balance;
    }
    // 获取剩余天数
    function getRemainDays() public view returns(uint) {
        return (endTime - now) / 60 / 60 / 24;
    }
    // 检查众筹状态
    function checkStatus() public onlyManagerCanCall {
        require(!flag);
        
        // 到期
        require(now >= endTime);
        // getTotalBalance金额>= goalMoney
        require(address(this).balance >= goalMoney);
        
        flag = true;
    }
    
    modifier onlyManagerCanCall {
        require(msg.sender == manager);
        _;
    }
}

猜你喜欢

转载自blog.csdn.net/www294993741/article/details/83995569