以太坊众筹 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
补充完整代码(优化后)
- 合约的方法调用其他合约方法 (获取我(参与者)参与的所有项目) 顺序整理:
- 存储参与者的众筹项目
- 创造PlayerToFundings合约, 用于保存每个用户的所有参与的项目
- 在FundingFactory的构造函数(constructor)中初始化这个合约p2f
- 众筹创建者发起众筹(FundingFactory.createFunding)时, 把这个合约的引用p2f传给每一个众筹项目
- 当路人参与众筹(Funding.support)时, 调用p2f的join函数, 保存这个参与者的众筹项目
- 读取自己参与的所有众筹项目
- 参与者调用FundingFactory的getPlayerFundings函数
- 由于FundingFactory没有保存相关参与数据, 所以转调了p2f的getPlayerFundings
- p2f把其保存的playerToFundings[player]信息(指定参与者的参与的所有项目)返回给FundingFactory
- 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);
_;
}
}