下面开始实现众筹平台的开发流程详解
npm install create -react-app -g
创建react项目
create-react-app lottery-eth
测试是否安装成功,进入目录
npm run start
npm i web3
npm i solc
npm i mocha
npm install --save semantic-ui-css
npm install --save semantic-ui-react
测试各个环境是否整合,开始开发…gogogogoo
下载本地版本巧克力
npm i ganache-cli - g
启动本地巧克力
ganache-cli -p 8545
众筹智能合约
pragma solidity^0.4.24;
contract FundingFactory {
address public platformManager; //平台的管理员
address[] fundingsAarry; //存储所有已经创建好的合约地址
mapping(address => address[]) creatorFundingsArray; //找到项目方所创建的所有众筹项目:
//mapping(address => address[]) supportFundingsArray; //找到所有自己参与过的合约项目
SupporterFunding supporterFunding; //地址是零,一定要实例化
constructor() {
platformManager = msg.sender;
supporterFunding = new SupporterFunding();//一定要实例化
}
//提过一个创建合约的方法
function createFunding(string _projectName, uint256 _supportMoney, uint256 _targetMoney, uint256 _durationTime) {
address funding = new Funding(_projectName, _supportMoney, _targetMoney,
_durationTime, msg.sender, supporterFunding);
fundingsAarry.push(funding);
creatorFundingsArray[msg.sender].push(funding); //维护项目发起人的所有众筹集合
}
function getAllFundings() public view returns(address[]) {
return fundingsAarry;
}
function getCreatorFundings() public view returns(address[]) {
return creatorFundingsArray[msg.sender];
}
function getSupporterFundings() public view returns(address[]) {
return supporterFunding.getFunding(msg.sender);
}
}
contract SupporterFunding {
mapping(address => address[]) supportFundingsArray; ////找到所有自己参与过的合约项目
//提供一个添加方法, //--> 在support时候调用
function joinFunding(address addr, address funding) {
supportFundingsArray[addr].push(funding);
}
//提供一个读取方法,
function getFunding(address addr) public returns (address[]) {
return supportFundingsArray[addr];
}
}
//crow-funding
contract Funding {
address public manager;
// 1. 名字:众筹牙刷
string public projectName;
// 2. 需要每个投资人投多少钱:38元
uint256 public supportMoney;
// 3. 总共要集资多少钱:100000
uint256 public targetMoney;
// 4. 众筹截止时间:30天 //2592000s
uint256 endTime;
mapping(address=> bool) supporterExistMap;
SupporterFunding supporterFunding;
// constructor(string _projectName, uint256 _supportMoney, uint256 _targetMoney,
// uint256 _durationTime, address _creator, mapping(address => address[]) _supportFundingsArray) public {
constructor(string _projectName, uint256 _supportMoney, uint256 _targetMoney,
uint256 _durationTime, address _creator, SupporterFunding _supporterFunding) public {
// manager = msg.sender;
manager = _creator; //把项目发起人的地址传递过来,否则就是合约工厂的地址了
projectName = _projectName;
supportMoney = _supportMoney;
targetMoney = _targetMoney;
endTime = now + _durationTime; //传递进来众筹持续的时间,单位为秒,now 加上这个值,就能算出项目截止时间
supporterFunding = _supporterFunding;
}
address[] supporters; //记录投资人的地址集合
//投资人投钱,记录投资的地址
function support() public payable {
require(msg.value == supportMoney); //wei
supporters.push(msg.sender);
//每次赞助之后,都使用一个map来记录投资人,便于后续快速检查
supporterExistMap[msg.sender] = true;
//supportFundingsArray[msgr]
//对传递过来的SupportFunding的结构进行赋值
supporterFunding.joinFunding(msg.sender, this);
}
//退款
function refund() onlyManager public {
for (uint256 i = 0; i< supporters.length; i ++) {
supporters[i].transfer(supportMoney);
}
//delete supporters;
}
enum RequestStatus { Voting, Approved, Completed}
//modifier
struct Request {
// 1. 要做什么:买电池
string purpose;
// 2. 卖家地址:0x1234xxx
address seller;
// 3. 多少钱:10元
uint256 cost;
// 4. 当前赞成的票数:10票
uint256 approveCount;
// 5. 有一个集合,记录投资人投票的状态:投过票,true,未投:false
mapping(address => bool) voteStatus; //每个地址智能support一次 //没有make(map[uint]string)
//voteStatus[0x111] = true
RequestStatus status;
}
//项目方可以创建多个请求,需要一个数组记录
Request[] public requests;
function createRequest(string _purpose, address _seller, uint256 _cost) onlyManager public {
Request memory req = Request({purpose: _purpose, seller: _seller, cost: _cost, approveCount: 0, status : RequestStatus.Voting});
requests.push(req);
}
//投资人批准:默认不投票:no,主动投票:yes
function approveRequest(uint256 index) public {
// 投资者调用approveRequest
// 1. 找到这个请求
// 限定:只有投资人才可以投票
// 2. 检查自己没有投过票
// 3. 投票
// 4. 标志自己已经投过票了
//bool flag = false;
// Flag 来记录遍历数组的结果,
// True表示:是投资人
// False表示:非投资人,直接退出
// require(supporterExistMap[msg.sender] == true);
require(supporterExistMap[msg.sender]);
Request storage req = requests[index];
//检查,如果不是投票状态(Compete或者Approved),就不用投票了
require(req.status == RequestStatus.Voting);
//这种使用for遍历的方式一定要小心, 尽量不要使用,消耗gas非常多
// for (uint i = 0 ; i < supporters.length; i++) {
// if (supporters[i] == msg.sender) {
// flag = true;
// break;
// }
// // break;
// }
// require(flag);
require(req.voteStatus[msg.sender] == false);
req.approveCount++;
req.voteStatus[msg.sender] = true;
}
//项目方可以花费这笔钱。
function finalizeReqeust(uint256 index) onlyManager public {
Request storage req = requests[index];
// 1. 检查当前余额是否满足支付
require(address(this).balance >= req.cost);
// 2. 检查赞成人数是否过半
require(req.approveCount *2 > supporters.length);
// 3. 向卖家转账
req.seller.transfer(req.cost);
// 4. 改变这个request的状态为Completed
req.status = RequestStatus.Completed;
}
modifier onlyManager {
require(msg.sender == manager);
_;
}
function getBalance() public view returns(uint256) {
return address(this).balance;
}
function getSupporters() public view returns(address[]) {
return supporters;
}
}
编写智能合约的步骤就略过了
这个文件的目的是为了编译sol文件,编译成二进制文件等
let solc = require('solc')
let fs = require('fs')
//.sol合约
let source = fs.readFileSync('./contracts/fundingFactory.sol', 'utf-8')
// console.log('source :', source)
let output = solc.compile(source, 1)
// console.log('output :', output)
module.exports = output['contracts'][':FundingFactory']
这个文件的目的是为了把编译后的文件部署到本地的区块链即巧克力,返回abi和合约地址
let Web3 = require('web3')
let {interface, bytecode} = require('./compile')
let web3 = new Web3()
web3.setProvider(new Web3.providers.HttpProvider('http://localhost:8545'))
deploy = async () => {
try {
//获取部署账户
accounts = await web3.eth.getAccounts()
contractInstance = await new web3.eth.Contract(JSON.parse(interface)).deploy(
{
data: bytecode
// arguments: ['']// 构造函数如果没有参数,就一定不要传
}
).send({
from: accounts[0],
gas: '5000000' //1000000太少了,无法完成部署,1千万又超过了区块的容量
})
console.log('address :', contractInstance.options.address)
} catch (e) {
console.log(e)
}
}
deploy()
首先要开启本地巧克力服务器
ganache-cli -p 8545
编译sol文件,产生二进制编码格式文件
node compile.js
部署智能合约文件,返回abi和合约地址
node deploy.js
联系第二部分
这里主要是前端页面通过web3.js调用api,用的是react框架
let solc = require('solc')
let fs = require('fs')
//.sol合约
let source = fs.readFileSync('./contracts/fundingFactory.sol', 'utf-8')
// console.log('source :', source)
let output = solc.compile(source, 1)
// console.log('output :', output)
module.exports = output['contracts'][':FundingFactory']
let Web3 = require('web3')
let {interface, bytecode} = require('./compile')
let web3 = new Web3()
web3.setProvider(new Web3.providers.HttpProvider('http://localhost:8545'))
deploy = async () => {
try {
//获取部署账户
accounts = await web3.eth.getAccounts()
contractInstance = await new web3.eth.Contract(JSON.parse(interface)).deploy(
{
data: bytecode
// arguments: ['']// 构造函数如果没有参数,就一定不要传
}
).send({
from: accounts[0],
gas: '5000000' //1000000太少了,无法完成部署,1千万又超过了区块的容量
})
console.log('address :', contractInstance.options.address)
} catch (e) {
console.log(e)
}
}
deploy()
App.js
import React, { Component } from 'react';
import web3 from './utils/getWeb3'
import {fundingFactoryContract} from './eth/contracts'
import TabExampleBasic from "./display/centerTab";
class App extends Component {
constructor() {
super()
this.state= {
platformManager : '',
currentAccount : '',
}
}
async componentDidMount() {
let accounts = await web3.eth.getAccounts()
let platformManager = await fundingFactoryContract.methods.platformManager().call({
from : accounts[0]
})
this.setState({
currentAccount : accounts[0],
platformManager,
})
}
render() {
let {platformManager, currentAccount, allFundings} = this.state
return (
<div className="App">
<p>平台管理员地址: {platformManager}</p>
<p>当前账户地址: {currentAccount}</p>
<p>当前所有合约地址: {allFundings}</p>
<TabExampleBasic/>
</div>
);
}
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import 'semantic-ui-css/semantic.min.css'
ReactDOM.render(<App />, document.getElementById('root'));
CardList.js
import React from 'react'
import { Card, Image, Icon, Progress, List } from 'semantic-ui-react'
const CardList = (props) => {
let {details, onItemClick} = props
console.table(details)
//根据details数组的个数,返回响应Card
let finalCards = details.map((detail, index) => {
return <MyCard key={index} detail={detail} onItemClick={onItemClick}/>
})
return (
<Card.Group itemsPerRow={4}>
{
finalCards
}
</Card.Group>
)
}
let MyCard = (props) => {
// resolve([fundingAddress, projectName, supportMoney, targetMoney, balance, supporters, leftTime, supportersCount])
let {0:fundingAddress, 1:projectName, 2:supportMoney, 3:targetMoney, 4:balance, 5:supporters, 6:leftTime, 7:supportersCount} = props.detail
console.log(fundingAddress, projectName, supportMoney, targetMoney, balance, supporters)
return (
<Card onClick={()=> props.onItemClick(props.detail)}>
<Image src='/images/logo.jpg'/>
<Card.Content>
<Card.Header>{projectName}</Card.Header>
<Card.Meta>
<span>剩余时间:{leftTime}秒</span>
<Progress indicating percent={balance / targetMoney * 100} size='small' progress/>
</Card.Meta>
<Card.Description>不容错过</Card.Description>
</Card.Content>
<Card.Content extra>
<List divided horizontal style={{display: 'flex', justifyContent: 'space-around'}}>
<List.Item>
<List.Content>
<List.Header>已筹</List.Header>
<List.Description>{balance}wei</List.Description>
</List.Content>
</List.Item>
<List.Item>
<List.Content>
<List.Header>已达</List.Header>
<List.Description>{balance / targetMoney * 100}%</List.Description>
</List.Content>
</List.Item>
<List.Item>
<List.Content>
<List.Header>参与人数</List.Header>
<List.Description>{supportersCount}人</List.Description>
</List.Content>
</List.Item>
</List>
</Card.Content>
</Card>
)
}
export default CardList
centerTab.js
import React from 'react'
import { Tab } from 'semantic-ui-react'
import AllFundingTab from './allFundingTab/allFundingTab'
import CreatorFundingTab from './creatorFundingTab/creatorFundingTab'
import SupportFundingTab from './supportFundingTab/supportFundingTab'
const panes = [
{ menuItem: '所有的', render: () => <Tab.Pane><AllFundingTab/></Tab.Pane> },
{ menuItem: '我发起的', render: () => <Tab.Pane><CreatorFundingTab/></Tab.Pane> },
{ menuItem: '我参与的', render: () => <Tab.Pane><SupportFundingTab/></Tab.Pane> },
]
const TabExampleBasic = () => <Tab panes={panes} />
export default TabExampleBasic
requestList.js
import React from 'react'
import {Table, Button} from 'semantic-ui-react'
const RequestList = (props) => {
let {requests, supportersCount, tabKey, onApproveClick, onFinalizeClick} = props
//requests是一个数组,使用map遍历它,然后每一个reqest生成一个Table.Row
let finalTableRow = requests.map((request, index) => {
console.log("xxxxxxxxx","index :", index, typeof onApproveClick)
//return (req.purpose, req.cost, req.seller, isVoted, req.approveCount, uint(req.status));
let {0:purpose, 1:cost, 2:seller, 3:isVoted, 4:approveCount, 5:status} = request
//status : 0-> 投票中 1-> 已批准 2-> 已完成
let isCompleted = false
let showStatus
if (status == 2) {
isCompleted = true;
showStatus = '已完成!';
} else if (supportersCount > approveCount * 2 ) {
showStatus = '投票中...';
} else {
showStatus = '已批准!';
}
return (
<Table.Row key={index}>
<Table.Cell>{purpose}</Table.Cell>
<Table.Cell>{cost}</Table.Cell>
<Table.Cell>{seller}</Table.Cell>
<Table.Cell>{approveCount}</Table.Cell>
<Table.Cell>{showStatus}</Table.Cell>
<Table.Cell>
{
(tabKey == 2) ? (
<Button disabled={approveCount * 2 < supportersCount ||isCompleted} onClick={() => onFinalizeClick(index)}>支付</Button>
) : (
<Button disabled={isVoted || isCompleted} onClick={() => onApproveClick(index)}>批准</Button>
)
}
</Table.Cell>
</Table.Row>
)
})
return (
<Table celled>
<Table.Header>
<Table.Row>
<Table.HeaderCell>花费详情</Table.HeaderCell>
<Table.HeaderCell>花费金额</Table.HeaderCell>
<Table.HeaderCell>消费商家</Table.HeaderCell>
<Table.HeaderCell>当前批准人数</Table.HeaderCell>
<Table.HeaderCell>当前状态</Table.HeaderCell>
<Table.HeaderCell>操作</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
finalTableRow
}
</Table.Body>
</Table>
)
}
export default RequestList
allFundingTab.js
import React, {Component} from 'react';
import {getFundingDetailArrayByTabId, supportFunding} from "../../eth/interaction";//三级文件
import CardList from '../CardList'//三级文件
import {Dimmer, Form, Label, Loader, Segment} from 'semantic-ui-react'
//继承函数
class AllFundingTab extends Component {
//构造器
constructor() {
super()
//系统固定对接参数
this.state = {
allFundings: [],//数组
selectedFundingDetail: '',//空值
active: false,//bool
}
}
//异步函数
async componentDidMount() {
try {
//调用三级函数
let allFundings = await getFundingDetailArrayByTabId(1)
//如果成功,放入中转站
this.setState({
allFundings,
})
} catch (e) {
//失败抛出异常
console.log(e)
}
}
//1. 在主界面传递一个回调函数onItemClick给CardList,用于返回用户点击的合约的详情
//2. 在CardList中传给MyCard
//3. 在MyCard中,当触发onClick的时候,调用这个onItemClick,返回相应的detail
//异步函数
handleSupport = async () => {
try {
//存入中转站
this.setState({active: true});
//解构中转站中的参数
const {
0: fundingAddress,
1: projectName,
2: supportMoney} = this.state.selectedFundingDetail;
//解构确认完成后,输出下,确保解构正常
console.log('funding :', fundingAddress);
// const {selectedFunding} = this.state;
//调用三级函数接收其返回值
let result = await supportFunding(fundingAddress, supportMoney);
//返回数据放入中转站
this.setState({active: false});
alert(`参与众筹成功\n项目名称:${projectName}\n项目地址:${fundingAddress}\n支持金额:${supportMoney}`);
console.log('invest successfully:\n', result);
} catch (e) {
this.setState({active: false});//异常处理
console.log(e);
}
}
//成为
render() {
//解构,取出中转站中的参数
let {allFundings, selectedFundingDetail} = this.state
//二次解构其中的变量,解析成使用数据
let {0: fundingAddress, 1: projectName, 2: supportMoney, 3: targetMoney, 4: balance, 5: supporters, 6: leftTime, 7: supportersCount} = selectedFundingDetail
return (
<div className="App">
<CardList details={allFundings} onItemClick={(detail) => {
console.log(detail)
this.setState({selectedFundingDetail: detail})
}}/>
<h2>参与众筹</h2>
{
selectedFundingDetail && (
<Dimmer.Dimmable as={Segment} dimmed={this.state.active}>
<Dimmer active={this.state.active} inverted>
<Loader>支持中</Loader>
</Dimmer>
<Form onSubmit={this.handleSupport}>
<Form.Input type='text' value={projectName} label='项目名称:'/>
<Form.Input type='text' value={fundingAddress} label='项目地址:'/>
<Form.Input type='text' value={supportMoney} label='支持金额:'
labelPosition='left'>
<Label basic>¥</Label>
<input/>
</Form.Input>
<Form.Button primary content='参与众筹'/>
</Form>
</Dimmer.Dimmable>
)
}
</div>
);
}
}
export default AllFundingTab;
creatorFundingTab.js
import React, { Component } from 'react';
import {createFunding, createRequest, getFundingDetailArrayByTabId, getAllRequests, finalizeRequest} from "../../eth/interaction";
import CardList from "../CardList";
import {Dimmer, Form, Label, Segment, Loader, Button} from 'semantic-ui-react'
import RequestList from "../requestList";
class CreatorFundingTab extends Component {
constructor() {
super()
this.state = {
creatorFundings : [],
active: false,
projectName: '',
supportMoney : '',
targetMoney : '',
durationTime : '',
selectedFundingDetail :'',
cost : '',
seller: '',
purpose:'',
requests: [],
}
}
async componentDidMount() {
try {
let creatorFundings = await getFundingDetailArrayByTabId(2)
this.setState({
creatorFundings,
})
} catch (e) {
console.log(e)
}
}
handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleCeateFunding = async () => {
console.log('创建众筹中!')
let {projectName, supportMoney, targetMoney, durationTime} = this.state
console.log('项目名称:',projectName, "支持金额:", supportMoney, "目标金额:", targetMoney, "持续时间:", durationTime)
try {
await createFunding(projectName, supportMoney, targetMoney, durationTime)
alert("创建众筹成功!")
window.location.reload(true)
} catch (e) {
alert("创建众筹失败!")
console.log(e)
}
}
handleCreateRequest = async () => {
//通过表单取到的数据
let {purpose, cost, seller} = this.state;
//点击取到的数据
let {0:fundingAddress} = this.state.selectedFundingDetail
console.log(purpose, cost, seller, fundingAddress)
try {
let result = await createRequest(fundingAddress, purpose, cost, seller);
console.log(result);
alert(`创建支付申请成功!`);
} catch (e) {
console.log(e);
}
}
getRequests = async () => {
let {0:fundingAddress} = this.state.selectedFundingDetail
try {
let requests = await getAllRequests(fundingAddress)
this.setState({requests})
console.log('requests 2222222222', requests)
} catch (e) {
console.log(e)
}
}
onFinalizeClickFunc = async (index) => {
try {
console.log('click index :', index);
let {0:fundingAddress} = this.state.selectedFundingDetail
await finalizeRequest(fundingAddress, index);
} catch (e) {
console.log(e)
}
}
render() {
let {creatorFundings, active, durationTime, supportMoney,
targetMoney, projectName, selectedFundingDetail, purpose, cost, seller,
requests,
} = this.state
let {7:supportersCount} = selectedFundingDetail
console.table(creatorFundings)
return (
<div className="App">
<p>{creatorFundings}</p>
<CardList details={creatorFundings} onItemClick={(detail) => {
console.log(detail)
this.setState({selectedFundingDetail: detail})
}}/>
<div>
<Dimmer.Dimmable as={Segment} dimmed={active}>
<Dimmer active={active} inverted>
<Loader>Loading</Loader>
</Dimmer>
<Form onSubmit={this.handleCeateFunding}>
<Form.Input required type='text' placeholder='项目名称' name='projectName'
value={projectName} label='项目名称:'
onChange={this.handleChange}/>
<Form.Input required type='text' placeholder='支持金额' name='supportMoney'
value={supportMoney} label='支持金额:'
labelPosition='left'
onChange={this.handleChange}>
<Label basic>¥</Label>
<input/>
</Form.Input>
<Form.Input required type='text' placeholder='目标金额' name='targetMoney' value={targetMoney}
label='目标金额:'
labelPosition='left'
onChange={this.handleChange}>
<Label basic>¥</Label>
<input/>
</Form.Input>
<Form.Input required type='text' placeholder='目标金额' name='durationTime' value={durationTime}
label='众筹时间:'
labelPosition='left'
onChange={this.handleChange}>
<Label basic>S</Label>
<input/>
</Form.Input>
<Form.Button primary content='创建众筹'/>
</Form>
</Dimmer.Dimmable>
</div>
{
selectedFundingDetail && (
<div>
<br/>
<h3>发起付款请求</h3>
<Segment>
<h4>当前项目:{selectedFundingDetail.projectName},
地址: {selectedFundingDetail.fundingAddress}</h4>
<Form onSubmit={this.handleCreateRequest}>
<Form.Input type='text' name='purpose' required value={purpose}
label='请求描述' placeholder='请求描述' onChange={this.handleChange}/>
<Form.Input type='text' name='cost' required value={cost}
label='付款金额' labelPosition='left' placeholder='付款金额'
onChange={this.handleChange}>
<Label basic>¥</Label>
<input/>
</Form.Input>
<Form.Input type='text' name='seller' required value={seller}
label='商家收款地址' labelPosition='left' placeholder='商家地址'
onChange={this.handleChange}>
<Label basic><span role='img' aria-label='location'></span></Label>
<input/>
</Form.Input>
<Form.Button primary content='开始请求'/>
</Form>
</Segment>
</div>)
}
{
selectedFundingDetail && (
<div>
<Button onClick={this.getRequests}>申请详情</Button>
<RequestList requests={requests}
supportersCount={supportersCount}
onFinalizeClick={this.onFinalizeClickFunc}
tabKey={2}
/>
</div>
)
}
</div>
);
}
}
export default CreatorFundingTab;
supportFundingTab.js
import React, { Component } from 'react';
import {getAllRequests, getFundingDetailArrayByTabId, approveRequest} from "../../eth/interaction";
import CardList from '../CardList'
import {Button} from 'semantic-ui-react'
import RequestList from "../requestList";
class SupportFundingTab extends Component {
constructor() {
super()
this.state = {
supporterFundings: [],
selectedFundingDetail : '',
requests : [],
}
}
async componentDidMount() {
try {
let supporterFundings = await getFundingDetailArrayByTabId(3)
this.setState({
supporterFundings,
})
} catch (e) {
console.log(e)
}
}
getRequests = async () => {
let {0:fundingAddress} = this.state.selectedFundingDetail
try {
let requests = await getAllRequests(fundingAddress)
this.setState({requests})
console.log('requests 2222222222', requests)
} catch (e) {
console.log(e)
}
}
onApproveClickFunc= async (index) => {
try {
console.log('click index :', index);
let {0:fundingAddress} = this.state.selectedFundingDetail
await approveRequest(fundingAddress, index);
} catch (e) {
console.log(e)
}
}
render() {
let {supporterFundings, selectedFundingDetail, requests} = this.state
let {7:supportersCount} = selectedFundingDetail
return (
<div className="App">
<CardList details={supporterFundings} onItemClick={(detail) => {
console.log(detail)
this.setState({selectedFundingDetail: detail})
}}/>
{
selectedFundingDetail && (
<div>
<Button onClick={this.getRequests}>申请详情</Button>
<RequestList requests={requests}
supportersCount={supportersCount}
tabKey={3}
onApproveClick={this.onApproveClickFunc}
/>
</div>
)
}
</div>
);
}
}
export default SupportFundingTab;
contracts.js
// let Web3 = require('web3')
// let web3 = new Web3()
//
// web3.setProvider(new Web3.providers.HttpProvider('http://localhost:8545'))
import web3 from '../utils/getWeb3'
//通过web3,找到我们已经部署好的合约实例
//ABI
//合约的地址
const fundingFactoryABI = [ { "constant": true, "inputs": [], "name": "platformManager", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_projectName", "type": "string" }, { "name": "_supportMoney", "type": "uint256" }, { "name": "_targetMoney", "type": "uint256" }, { "name": "_durationTime", "type": "uint256" } ], "name": "createFunding", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getAllFundings", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getSupporterFundings", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getCreatorFundings", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "inputs": [], "payable": false, "stateMutability": "nonpayable", "type": "constructor" } ]
const fundingABI = [ { "constant": false, "inputs": [], "name": "support", "outputs": [], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": true, "inputs": [], "name": "getBalance", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_purpose", "type": "string" }, { "name": "_seller", "type": "address" }, { "name": "_cost", "type": "uint256" } ], "name": "createRequest", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getRequestCount", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getSupporters", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "manager", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "refund", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getSupportersCount", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "index", "type": "uint256" } ], "name": "getRequestByIndex", "outputs": [ { "name": "", "type": "string" }, { "name": "", "type": "uint256" }, { "name": "", "type": "address" }, { "name": "", "type": "bool" }, { "name": "", "type": "uint256" }, { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getLeftTime", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "requests", "outputs": [ { "name": "purpose", "type": "string" }, { "name": "seller", "type": "address" }, { "name": "cost", "type": "uint256" }, { "name": "approveCount", "type": "uint256" }, { "name": "status", "type": "uint8" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "targetMoney", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "projectName", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "supportMoney", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "index", "type": "uint256" } ], "name": "approveRequest", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "index", "type": "uint256" } ], "name": "finalizeReqeust", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "name": "_projectName", "type": "string" }, { "name": "_supportMoney", "type": "uint256" }, { "name": "_targetMoney", "type": "uint256" }, { "name": "_durationTime", "type": "uint256" }, { "name": "_creator", "type": "address" }, { "name": "_supporterFunding", "type": "address" } ], "payable": false, "stateMutability": "nonpayable", "type": "constructor" } ]
const fundingFactoryContractAddress = '0xb23d3b317849a7f84b787fe04efddfdadd3bfdb0'
let fundingFactoryContract = new web3.eth.Contract(fundingFactoryABI, fundingFactoryContractAddress)
//地址在程序获取,并且添加 fundingContract.options.address = '0x111'
let fundingContract = new web3.eth.Contract(fundingABI)
//每次调用这个函数时,总创建一个新的合约实例
let newFundingContract = () => { return new web3.eth.Contract(fundingABI) }
// export default lotteryContract
export {
fundingFactoryContract,
fundingContract,
newFundingContract,
}
interaction.js
//interaction 交互
import {fundingFactoryContract, newFundingContract} from "./contracts";
import web3 from "../utils/getWeb3";
//index = 1, 代表第一个页面,获取所有的合约
//index = 2, 代表第二个页面,获取创建者的合约
//index = 3, 代表第三个页面,获取参与的合约
let getFundingDetailArrayByTabId = (index) => {
//自定义一个promise,封装自己的业务逻辑
return new Promise((async (resolve, reject) => {
try {
let accounts = await web3.eth.getAccounts()
let fundings = []
//众筹合约的地址集合:飞机, 航母
if (index === 1) {
fundings = await fundingFactoryContract.methods.getAllFundings().call({
from: accounts[0]
})
} else if (index === 2) {
fundings = await fundingFactoryContract.methods.getCreatorFundings().call({
from: accounts[0]
})
} else if (index === 3) {
fundings = await fundingFactoryContract.methods.getSupporterFundings().call({
from: accounts[0]
})
} else {
reject("输入错误")
}
// let fundingDetails = [] //所有合约的详细信息的集合
//这种使用for循环的方式不高效, 因为渲染获取数据是顺序的
// for (let i = 0 ; i< creatorFundings.length; i++ ) {
// let fundingDetail = await getFundingDetail(creatorFundings[i])
// fundingDetails.push(fundingDetail)
// }
//map里面是一个函数,有两个参数,第一个是遍历的数据,第二个是索引值
// creatorFundings = [1,2,3,4]
// let rest = creatorFundings.map((value, i) => {return value + 1})
// rest = [2,3,4,5]
//返回一个detail promise的集合
let detailsArray = fundings.map(creatorFunding => getFundingDetail(creatorFunding))
//这个方法,将所有的promise转换为一个promise
let fundingDetailsRes = Promise.all(detailsArray)
// console.table(fundingDetails)
// let a = typeof fundingDetails
// console.log(`${a}`)
//成功时调用resolve,返回正确数据
resolve(fundingDetailsRes)
} catch (e) {
//失败时调用reject,返回错误信息
reject(e)
}
}))
}
let getFundingDetail = (fundingAddress) => {
return new Promise(async (resolve, reject) => {
//众筹合约的地址
//这个fundingContract已经完整,可以调用了
try {
//这个fundingContract全局唯一,在进行map处理的时候,后面的合约地址将前面的给覆盖了,导致返回总是最后一个合约的数据
// fundingContract.options.address = fundingAddress
//解决办法:每一个合约都创建一个新的fundingContract
let fundingContractNew = newFundingContract()
// console.log('new :', fundingContractNew)
fundingContractNew.options.address = fundingAddress
let accounts = await web3.eth.getAccounts()
//根据合约的实例,获取这个合约的详细信息
//合约里面的public修饰的状态变量(成员)会默认的生成一个访问函数(Getter Function)
let manager = await fundingContractNew.methods.manager().call({from: accounts[0]})
let projectName = await fundingContractNew.methods.projectName().call({from: accounts[0]})
let supportMoney = await fundingContractNew.methods.supportMoney().call({from: accounts[0]})
let targetMoney = await fundingContractNew.methods.targetMoney().call({from: accounts[0]})
let balance = await fundingContractNew.methods.getBalance().call({from: accounts[0]})
let supporters = await fundingContractNew.methods.getSupporters().call({from: accounts[0]})
let leftTime = await fundingContractNew.methods.getLeftTime().call({from: accounts[0]})
let supportersCount = await fundingContractNew.methods.getSupportersCount().call({from: accounts[0]})
// console.log('111111 manager :', manager)
// resolve({manager, projectName, supportMoney, targetMoney, balance, supporters})
resolve([fundingAddress, projectName, supportMoney, targetMoney, balance, supporters, leftTime, supportersCount])
// console.table(fundingDetails)
} catch (e) {
reject(e)
}
})
}
const createFunding = (projectName, supportMoney, targetMoney, durationTime) => {
return new Promise(async (resolve, reject) => {
try {
let accounts = await web3.eth.getAccounts();
let result = await fundingFactoryContract.methods.createFunding(projectName, supportMoney, targetMoney, durationTime).send({
from: accounts[0]
});
resolve(result);
} catch (e) {
reject(e);
}
})
}
const supportFunding = (funding, supportMoney) => {
return new Promise(async (resolve, reject) => {
try {
let accounts = await web3.eth.getAccounts();
//创建新的合约实例,将点击的众筹项目地址传递进去,然后才能发起参与
let fundingContract = newFundingContract();
fundingContract.options.address = funding;
let result = await fundingContract.methods.support().send({
from: accounts[0],
//参与要传递相应的金额
value: supportMoney,
});
resolve(result);
} catch (e) {
reject(e);
}
})
}
const createRequest = (fundingAddress, purpose, cost, seller) => {
return new Promise(async (resolve, reject) => {
try {
let accounts = await web3.eth.getAccounts();
let fundingContract = newFundingContract();
fundingContract.options.address = fundingAddress;
let result = await fundingContract.methods.createRequest(purpose, seller, cost).send({
from: accounts[0],
});
console.log(result)
resolve(result);
} catch (e) {
reject(e);
}
})
}
const getAllRequests = (fundingAddress) => {
return new Promise(async (resolve, reject) => {
try {
let accounts = await web3.eth.getAccounts();
let fundingContract = newFundingContract();
fundingContract.options.address = fundingAddress;
//拿到请求的数量: 3
let requestCount = await fundingContract.methods.getRequestCount().call({
from: accounts[0],
});
let requestDetails = [];
for (let i = 0; i < requestCount; i++) {
let requestDetail = await fundingContract.methods.getRequestByIndex(i).call({
from: accounts[0],
});
requestDetails.push(requestDetail);
}
resolve(requestDetails);
} catch (e) {
reject(e);
}
})
}
const approveRequest = (address, index) => {
return new Promise(async (resolve, reject) => {
try {
const accounts = await web3.eth.getAccounts();
const account = accounts[0];
console.log(`account: ${account}`);
console.log(`funding: ${address}`);
console.log(`index: ${index}`);
const contract = newFundingContract();
contract.options.address = address;
const result = await contract.methods.approveRequest(index).send({
from: account,
});
console.log('result :', result);
resolve(result);
} catch (e) {
reject(e);
}
})
};
const finalizeRequest = (address, index) => {
return new Promise(async (resolve, reject) => {
try {
const accounts = await web3.eth.getAccounts();
const account = accounts[0];
console.log(`account: ${account}`);
console.log(`funding: ${address}`);
console.log(`index: ${index}`);
const contract = newFundingContract();
contract.options.address = address;
const result = await contract.methods.finalizeReqeust(index).send({
from: account,
});
console.log('result :', result);
resolve(result);
} catch (e) {
reject(e);
}
})
};
export {
getFundingDetailArrayByTabId,
createFunding,
supportFunding,
createRequest,
getAllRequests,
approveRequest,
finalizeRequest,
}
getWeb3.js
let Web3 = require('web3')
let web3
// web3.setProvider(new Web3.providers.HttpProvider('http://localhost:8545'))
//如果当前的页面没有web3,那么尝试连接本地web3服务
if (typeof window.web3 === 'undefined') {
console.log('local web3 found!')
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
} else {
//如果当前的页面有web3,那么就连接它,我们的应用,一定给用户用的,用户要使用自己的web3
web3 = new Web3(window.web3.currentProvider)
console.log('injected web3 found!')
}
export default web3