以太坊彩票项目

   日期:2020-08-07     浏览:94    评论:0    
核心提示:以太坊彩票项目目录项目概述彩票业务规则-编写智能合约lottery.sol[1].彩票业务规则示例图[2].整体项目搭建[3].彩票合约 lottery.js[4].编译合约 01-compile.js[5].部署合约 02-deploy.js[6]. 从区块链获取合约实例[7].从区块链获取合约实例功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注

以太坊彩票项目

  • 目录
    • 项目概述
    • 项目具体实现
      • [1].彩票业务规则示例图
      • [2].整体项目搭建
      • [3].彩票合约 lottery.sol
      • [4].编译合约 01-compile.js
      • [5].部署合约 02-deploy.js
      • [6]. 从区块链获取合约实例
      • [7].完善界面
      • [8].最终效果
    • 创作声明
    • 备注

目录

项目概述

solidity 编写合约,node.js 编译、部署、获取、交互合约,react搭建前端界面
超详细~

(1)彩票业务规则-智能合约lottery.sol
[1] 全民参与(play函数)
[2] 每次投注只能投注1eth
[3] 每个人可以投多注
[4] 仅限管理员可以开奖(KaiJiang函数)
[5] 仅限管理员可以退奖(TuiJiang函数)
(2)编译智能合约 01-compile.js
[1] 导入solc编译器和fs库
[2] fs读取contracts文件夹下lottery.sol合约
[3] solc编译合约
[4] 导出bytecode(机器码)和interface(ABI)
(3)部署智能合约上链 02-deploy.js
[1] 获取bytecode和interface
[2] 导入web3
[3] 设置网络,管理员(部署合约的人)实例化web3(.setProvider)
[4] 拼接合约数据
[5] 拼接bytecode
[6] 返回合约地址(instanceAddress)
(4)从区块链获取合约实例
[1] 获取interface(ABI)和合约地址(instanceAddress)
[2] 导入web3
[3] 并设设置网络,用户(使用者)实例化web3(.setProvider)
[4] 利用interface(ABI)和合约地址(instanceAddress)获取合约
[5] 返回合约,用于交互
(5)设计前端,同合约交互
[1] 利用react完成界面搭建
[2] 在界面中显示返回的数据
[3] 待输入

1.合约上链交互5部曲示意图

项目具体实现

[1].彩票业务规则示例图





[2].整体项目搭建

系统环境 : MacOS,IDE : Goland2020

  1. 整体项目结构图(环境搭建完毕图)
  2. 使用react脚手架

[step-1] 终端进入goland项目文件夹下: cd /XXXXXX/GoProjects/src/
[step-2] 终端安装react应用程序骨架: npm install -g create-react-app
[step-3] 终端创建一个空的项目: create-react-app lottery-react
[step-4] 在goland中打开此项目

  1. goland终端里输入npm start启动项目,最终效果如图

  2. 安装react组件库和配套样式库,并清理工程

[step-1] npm install semantic-ui-react --save
[step-2] npm install semantic-ui-css --save
[step-3] 进⼊src⽬录,保留App.js和index.js,删除掉其他⽂件
[step-4] 删掉App.js和index.js中对删除⽂件的依赖代码
[step-5] 删掉App.js中render函数渲染的内容
[step-6] 手动输入萌新认证:Hello World
[step-7] 在goland中打开此项目

//App.js 清理后代码
import React, {Component} from 'react';

class App extends Component {
  render() {
    return(
        <p>Hello World !</p>
    );
  }
}

export default App;
//index.js 清理后代码
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
    <App />,
  document.getElementById('root')
);

最终效果图

  1. 安装solc编译器和web3模块,并配置项目

[step-1] npm install solc@0.4.25 --save
[step-2] npm install web3@1.2.11 --save
注意: 若版本不一致,可以通过 name@xxx,指定版本(这两个版本尽量保持一直,否则容易出问题)
[step-3] (我是mac版goland2020,位置同其他版可能不一样,请通过搜索引擎解决)如图配置
[step-4] 新建一个testEnvironment.js 文件,输入最后一幅图,结果如果所示,则配置完成
注意:新建文件后,若显示红色,为git为添加问题,右键文件,找到"Git",选择"+Add";或者上一步中,右下角选择"Always Add"。







  1. 最后安装的库 package.json
{
  "name": "review-lottery",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-scripts": "3.4.1",
    "semantic-ui-css": "^2.4.1",
    "semantic-ui-react": "^1.1.1",
    "solc": "^0.4.25",
    "web3": "^1.2.11"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
  1. 创建目录与文件

[step-1] goland终端输入图中命令,特别注意当前所在目录
[step-2]根目录创建合约目录contracts,创建合约lottery.sol文件,命令如图
最终效果如图


[3].彩票合约 lottery.sol

[step-1] Google浏览器,打开Remix在线编译器(选择0.4.24) http://remix.ethereum.org/
[step-2] 在线测试代码Lottery.sol

pragma solidity ^0.4.24;

contract Lottery{
    // ---整体逻辑
    //1. 管理员:负责开奖和退奖,添加modifier函数限定两个函数
    //2. 彩民池:address[] players,开奖退奖都要清空
    //3. 当前期数:每次开奖退奖后都要加1
    //4. 开奖:
    // (1)求随机数,确定中奖地址 --这里需要注意
    // (2)分配奖金池,分为奖金和管理费
    // (3)给中奖地址和管理员地址转钱
    // (4)期号+1,清空彩民池
    //5. 退奖:
    // (1)根据数组长度,for循环地址转钱
    // (2)期号+1,清空彩民池
    //6. 投注play:
    // (1)限制条件:require(要求投注金额必须是1个以太币)
    // (2)将投注地址添加到彩民池子
	
	//变量,这里public,下面也用getXXX形式返回了。
	//管理员地址
    address public manager;
    //中奖彩民
    address public winner;
    //第round期
    uint256 public round = 1;
    //所有参与的彩民(管理员也可以参与游戏)
    address[] public players;

	//构造函数,部署合约的人就是管理员
    constructor() public{
        manager = msg.sender;
    }

    //---彩民投注函数
    //1. 合约⾥⾯的单位默认为 wei ,需要进⾏ ether 修饰,也可以使⽤ 1 * 10 ** 18 进⾏转换。
    //2. payable关键字必须添加,否则⽆法购买 
    //3. msg.value全局变量能够接收到交易中的 value 字段 
    //4. 数组添加使⽤ push
    function play() payable public{
    	//require限定投注金额为1ETH
        require(msg.value == 1 ether);
        //将投注者添加到彩民池
        players.push(msg.sender);
    }
	
	//---管理员开奖函数,用onlyManager限定权限
	//1. 随机中奖需要⼀个随机的索引值,我们使⽤难度值,当前时间,参与⼈数作为种⼦⽣成⼀个⼤的 数字⽣成索引。
	//2. 开奖前要注意校验有效性,如果⽆⼈参与可以不开奖。 
	//3. 本轮结束后 round++ ,进⼊下⼀轮。 
	//4. 删除所欲的参与⼈,注意delete只是删除内部元素,不会删除 players 。
    function KaiJiang() onlyManager public{
		//随机一个下标值,表示获奖者
        bytes memory tmp1 = abi.encodePacked(block.timestamp, block.difficulty, players.length);
        bytes32 tmp2 = keccak256(tmp1);
        uint256 tmp3 = uint256(tmp2);
        
		//确定获奖者地址
        uint256 index = tmp3 % players.length;
        winner = players[index];
        
		//根据9-1分成规则转钱
        uint256 contractMoney = address(this).balance;
        uint256 winnerMoney = contractMoney / 100 * 90;
        uint256 managerMoney = contractMoney - winnerMoney;
        winner.transfer(winnerMoney);
        manager.transfer(managerMoney);
		
		//本期结束后期数+1,并清空彩民池
        round++;
        delete players;
    }
    
	//---管理员退奖
    function TuiJiang() onlyManager public{
    	//遍历数组,逐一转账
        for(uint i = 0; i < players.length; i++){
            players[i].transfer(1 ether);
        }
        //期数+1
        round++;
        //清空彩民池
        delete players;
    }
	
	//---修饰器
    modifier onlyManager{
    	//限定作用,非管理员不允许调用开奖和退奖函数
        require(msg.sender == manager);
        _;
    }

    //返回奖池金额
    function getBalance() view public returns(uint){
        return address(this).balance;
    }

    //返回奖池人数
    function getPlayersLength() view public returns(uint){
        return players.length;
    }

    //以数组形式返回奖池人地址
    function getPlayers() view public returns(address []){
        return players;
    }
	
	//返回管理员
    function getManager() view public returns(address){
        return manager;
    }
	
	//返回管理员
    function getWinner() view public returns(address){
        return winner;
    }
	
	//返回当前期号
    function getRound() view public returns(uint256){
        return round;
    }
}

[4].编译合约 01-compile.js

//1.导入相关包
let solc = require('solc')
let fs = require('fs')
//2.读取合约
let sourceCode = fs.readFileSync('./contracts/Lottery.sol', 'utf-8')
//3.编译合约
//将1设置为第二个参数将激活优化器
let  output = solc.compile(sourceCode, 1)
//4.导出合约
//console.log(output)
module.exports = output[ 'contracts'][':Lottery']

[5].部署合约 02-deploy.js

//1. 引入
let {bytecode, interface} = require('./01-compile')
let Web3 = require('web3')
//let HDWalletProvider = require('truffle-hdwallet-provider')

//2. new一个web3实例
let web3 = new Web3();

//3. 设置网络,这里用Ganache本地测试网络环境,自行搜索安装
//助记词
//let terms = '注意这里需要输入助记词';
let netIp = 'http://localhost:7545'
//let provider = new HDWalletProvider(terms, netIp)
//const web3 = new Web3();

web3.setProvider(netIp)

let contract = new web3.eth.Contract(JSON.parse(interface))


let deploy = async () => {
    //4. 先获取所有的账户
    let accounts = await web3.eth.getAccounts() //获取账户列表
    console.log('account:', accounts)
    //5. 执行部署
    let instance = await contract.deploy({
        data:bytecode, //合约的bytecode
        //argument : ['HelloWorld']
    }).send({
        from : accounts[0], //部署合约人(管理员地址),需要从这里扣钱!!!
        gas : '6721975',
        gasPrice : '1'
    })
    console.log('instance address : ', instance.options.address)
}

deploy()

[6]. 从区块链获取合约实例

initWeb3.js
注意重要更新,很重要

let Web3 = require('web3')
let web3 = new Web3();

//重要更新:为了保护隐私,默认关闭,需要手动开启,才能获取用户当前地址
//information : https://medium.com/metamask/https-medium-com-metamask-breaking-change-injecting-web3-7722797916a8
window.ethereum.enable();

//说明:
//实例化web3需要一个provider(服务商),管理员部署的时候用自己的账号
//但是彩民参与这个活动,投注要用自己账号的钱
//实现
//1.用Ganache的某个账户地址的私钥,在MetaMask中创建一个账户
//2.在浏览器启动后,MetaMask会向浏览器注入一个web3实例
//3.利用window.web3.currentProvider获得实例
web3.setProvider(window.web3.currentProvider)
console.log("Injected web3 found!")

module.exports = web3

lottery.js
注意:不要直接复制abi和address,将合约放到在线编译器http://remix.ethereum.org/中编译通过,从在线编译器中复制abi

let web3 = require('../utils/initWeb3')
//这里可以从remix solidity在线编译器中直接获取
const abi = [{
    "constant": false,
    "inputs": [],
    "name": "KaiJiang",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
}, {
    "constant": false,
    "inputs": [],
    "name": "play",
    "outputs": [],
    "payable": true,
    "stateMutability": "payable",
    "type": "function"
}, {
    "constant": false,
    "inputs": [],
    "name": "TuiJiang",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
}, {"inputs": [], "payable": false, "stateMutability": "nonpayable", "type": "constructor"}, {
    "constant": true,
    "inputs": [],
    "name": "getBalance",
    "outputs": [{"name": "", "type": "uint256"}],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "getManager",
    "outputs": [{"name": "", "type": "address"}],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "getPlayers",
    "outputs": [{"name": "", "type": "address[]"}],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "getPlayersLength",
    "outputs": [{"name": "", "type": "uint256"}],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "getRound",
    "outputs": [{"name": "", "type": "uint256"}],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "getWinner",
    "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": true,
    "inputs": [{"name": "", "type": "uint256"}],
    "name": "players",
    "outputs": [{"name": "", "type": "address"}],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "round",
    "outputs": [{"name": "", "type": "uint256"}],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "winner",
    "outputs": [{"name": "", "type": "address"}],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}]
//注意这里是合约地址,从deploy.js返回值中获取
const address = '0x6988851A341E179C29733e8C824D5658A5572507'

let lottery = new web3.eth.Contract(abi, address)
module.exports = lottery

[7].完善界面

ui.js

import React from 'react'
import { Card, Icon, Image, Statistic, Button, Label} from 'semantic-ui-react'


const CardExampleCard = (props) => (
    <Card>
        <Image src='images/logo.jpg' wrapped ui={false} />
        <Card.Content>
            <Card.Header>中国体育彩票</Card.Header>
            <Card.Meta>
                <p>管理员地址:</p>
                <Label size='mini'>
                    <Icon name='hand point right' />{props.manager}
                </Label>
                <p>当前地址:</p>
                <Label size='mini'>
                    <Icon name='hand point right' />{props.account}
                </Label>
            </Card.Meta>
            <Card.Description>
                每晚八点半准时开奖,不见不散!!!
            </Card.Description>
        </Card.Content>
        <Card.Content extra>
            <a>
                <Icon name='user' />
                {props.playerCounts}人参与
            </a>
        </Card.Content>

        <Card.Content extra>
            <Statistic color='red'>
                <Statistic.Value>{props.balance}ETH</Statistic.Value>
                <Statistic.Label>奖金池</Statistic.Label>
            </Statistic>
        </Card.Content>

        <Card.Content extra>
            <Statistic color='blue'>
                <Statistic.Value>{props.round}</Statistic.Value>
                <a href='www.baidu.com'>点我查看历史交易记录</a>
            </Statistic>
        </Card.Content>

        <Button animated='fade' color='orange' onClick={props.play} loading={props.isPlaying}>
            <Button.Content visible>投注产生希望</Button.Content>
            <Button.Content visible>购买放飞梦想</Button.Content>
        </Button>

        <Button inverted color='red' style={{display:props.isShowButton}} onClick={props.Kaijiang} loading={props.isDrawing}>
            开奖
        </Button>
        <Button inverted color='green' style={{display:props.isShowButton}} onClick={props.Tuijiang} loading={props.isDrawBacking}>
            退奖
        </Button>
    </Card>
)

export default CardExampleCard

App.js

import React,{Component} from 'react';
import CardExampleCard from './display/ui'
let web3 = require('./utils/initWeb3')
let lottery = require('./eth/lottery')

class App extends Component{

    constructor(props) {
        super(props);
        this.state = {
            manager : '',
            round:'',
            winner:'',
            playerCounts:0,
            balance:0,
            players:[],
            account : '',
        };
    }

    async componentWillMount(){
        let accounts = await web3.eth.getAccounts()
        let manager = await lottery.methods.manager().call();
        let round = await lottery.methods.getRound().call();
        let winner = await lottery.methods.getWinner().call();
        let playerCounts = await lottery.methods.getPlayersLength().call();
        let balanceWei = await lottery.methods.getBalance().call();
        let balance = web3.utils.fromWei(balanceWei, 'ether')
        let players = await lottery.methods.getPlayers().call();
        this.setState({
            manager,
            round,
            winner,
            playerCounts,
            balance,
            players,
            account:accounts[0],
            isPlaying:false,
            isDrawing:false,
            isDrawBacking:false,
            isShowButton:accounts[0] === manager ? 'inline' : 'none',
        });
    }

    play = async() =>{
        console.log("Play Button Clicked!!!");
        this.setState({isPlaying:true})
        try{
            let accouts = await web3.eth.getAccounts();
            await lottery.methods.play().send({
                from:accouts[0],
                value:1*10**18
            })
            alert(`Success: play successfully!`);
            this.setState({isPlaying:false});
            window.location.reload(true);
        }catch (e) {
           alert(`Error: play failed! ${e}`);
           this.setState({isPlaying:false});
        }
    }

    Kaijiang = async() =>{
        console.log("Kaijiang Button Clicked!!!");
        this.setState({isDrawing:true})
        try{
            let accouts = await web3.eth.getAccounts();
            await lottery.methods.KaiJiang().send({
                from:accouts[0],
            })
            let winner_ = await lottery.methods.getWinner().call();
            alert(`开奖喽!中奖者为${winner_}`);
            this.setState({isDrawing:false});
            window.location.reload(true);
        }catch (e) {
            alert(`Error: kaijiang failed! ${e}`);
            this.setState({isDrawing:false});
        }
    }
    Tuijiang = async() =>{
        console.log("Tuijiang Button Clicked!!!");
        this.setState({isDrawBacking:true})
        try{
            let accouts = await web3.eth.getAccounts();
            await lottery.methods.TuiJiang().send({
                from:accouts[0],
            })
            alert(`退奖成功!!!`);
            this.setState({isDrawBacking:false});
            window.location.reload(true);
        }catch (e) {
            alert(`Error: Tuijiang failed! ${e}`);
            this.setState({isDrawBacking:false});
        }
    }


    render() {
        return (
            <div>
                <CardExampleCard
                    manager={this.state.manager}
                    round={this.state.round}
                    winner={this.state.winner}
                    balance={this.state.balance}
                    players={this.state.players}
                    playerCounts={this.state.playerCounts}
                    account={this.state.account}
                    play={this.play}
                    Tuijiang={this.Tuijiang}
                    Kaijiang={this.Kaijiang}
                    isPlaying={this.state.isPlaying}
                    isDrawing={this.state.isDrawing}
                    isDrawBacking={this.state.isDrawBacking}
                    isShowButton={this.state.isShowButton}
                />
            </div>
        );
    }
}

export default App;

[8].最终效果

创作声明

本文是作者学习"传智播客"旗下"区块链"课程时,基于自己思考,实现并总结的学习笔记。
本文项目思路,图片源于"传智播客"的学习资料,若有不当,请联系删除。
若有需要,请支持并购买"传智播客"旗下正版学习资料。

备注

一枚区块链萌新,希望同大家多多学习交流!!!

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服