{"id":21532426,"url":"https://github.com/minecoinchain/lotterybyeth","last_synced_at":"2025-04-10T00:32:13.590Z","repository":{"id":219529916,"uuid":"203407998","full_name":"MineCoinChain/LotteryByETH","owner":"MineCoinChain","description":"以太坊DAPP开发-彩票","archived":false,"fork":false,"pushed_at":"2019-08-22T05:05:55.000Z","size":2598,"stargazers_count":53,"open_issues_count":1,"forks_count":19,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T02:13:04.649Z","etag":null,"topics":["eth"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MineCoinChain.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-08-20T15:52:46.000Z","updated_at":"2025-03-13T13:05:25.000Z","dependencies_parsed_at":"2024-01-28T05:07:10.398Z","dependency_job_id":null,"html_url":"https://github.com/MineCoinChain/LotteryByETH","commit_stats":null,"previous_names":["minecoinchain/lotterybyeth"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineCoinChain%2FLotteryByETH","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineCoinChain%2FLotteryByETH/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineCoinChain%2FLotteryByETH/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineCoinChain%2FLotteryByETH/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MineCoinChain","download_url":"https://codeload.github.com/MineCoinChain/LotteryByETH/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248135019,"owners_count":21053589,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["eth"],"created_at":"2024-11-24T02:19:53.627Z","updated_at":"2025-04-10T00:32:13.569Z","avatar_url":"https://github.com/MineCoinChain.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 以太坊DAPP开发-彩票的设计与实现\n\n## 一.项目背景\n\n​\t传统的彩票网站存在暗箱操作，容易贪污跑路的问题，而基于以太坊的彩票网站，则有着公开，公正，公平的优点。\n\n## 二.业务需求\n\n1.全民参与（任何地址都可以投注）\n\n2.每个人每次只能投1个ether（相当于2元1注）\n\n3.每个人可以买多注\n\n4.设置一个管理员，负责：\n\n- 定期开奖\n- 临时退奖（防止有特殊情况）\n\n## 三.项目框架图\n\n​\t![](https://ws2.sinaimg.cn/large/006tNbRwly1fv6xczcxj8j31fd0jv4a3.jpg)\n\n#### 项目模块交互演示图\n\n​\t![](https://ws2.sinaimg.cn/large/006tNbRwly1fv6xez0duwj31hc0z2u0x.jpg)\n\n## 四.合约设计\n\n### 合约需要的状态变量\n\n​\t   1.管理员：manager ，address类型\n\n​\t   2.记录所有的彩民的地址集合：players，address[]类型\n\n​\t   3.第几期：round  int\n\n​\t   4.上一期的中奖地址：winner，address\n\n### 合约中的方法\n\n​\t   1.参与投奖：play() payable（任何人都可以调用，调用时转入1ether到合约）\n\n​\t   2.管理员专用\n\n​\t\t\t-开奖：draw（）选择一个随机的地址，将合约的钱转入这个地址；\n\n​\t\t\t-退奖：undraw（）遍历所有的彩民，依次向彩民池中的地址转账，每人转账1ether。\n\n**注意点：**\n\n1. 退奖和开奖时需要花费的手续费是管理员地址账户中的钱；\n2. 投奖时花费的手续费是用户账户中的钱；\n3. 一句话：谁调用合约中的方法就花费谁的钱；\n4. 对于用户来说，进出都是1eth永远不变；\n\n### 合约代码\n\n```js\npragma solidity ^0.4.25;\n\ncontract Lottery{\n    //管理员地址\n    address public manager;\n    //所有彩民\n    address[] public players;\n    //彩票期数\n    uint public round;\n    //上一期中奖地址\n    address public winner;\n    //添加管理员地址\n    constructor() public{\n        manager=msg.sender;\n    }\n\n    //参与投奖\n    function play() public payable{\n        //要求调用时转入\n        require(msg.value==1 * 10 ** 18);\n        players.push(msg.sender);\n    }\n    //管理员开奖\n    function draw() public onlyManager{\n        //以太坊中没有提供随机数生成方法，可以随机生成一个哈希数，并将它对数组长度取余\n        //取当前的难度值，时间戳和彩民人数作为随机数的种子\n        bytes memory info = abi.encodePacked(block.difficulty,block.timestamp,players.length);\n        bytes32 hash = keccak256(info);\n        uint index = uint(hash)%players.length;\n        winner = players[index];\n        winner.transfer(address(this).balance);\n        //彩民池清空同时期数加1\n        delete players;\n        round++;\n    }\n    \n    //管理员退奖\n    function undraw() public onlyManager{\n        //遍历彩民数组，依次向彩民转账\n        for(uint256 i=0;i\u003cplayers.length;i++){\n            players[i].transfer(1 ether);\n        }\n        delete players;\n        round++;\n    }\n    /*******************辅助函数*************************/\n    //获取奖金池额度\n    function getBalance() public view returns(uint256){\n        return address(this).balance;\n    }\n    //获取当前彩民\n    function getPlayers() public view returns(address[]){\n        return players;\n    }\n    //添加修饰器\n    modifier onlyManager(){\n        require(msg.sender==manager);\n        _;\n    }\n}\n```\n\n## 五.前端部署（react）\t\n\n### **创建react空工程**\n\n```sh\nnpm  i -g  create-react-app //安装react脚手架\ncreate-react-app lottery\t//创建项目文件夹\nnpm run start //运行react空工程\n```\n\n### **清理react空工程**\n\n​\t![](./assets/react工程清理.png)\n\n​\tsrc文件夹下只保留App.js和index.js;\n\n​\tApp.js以及Index.js文件夹下保留的内容：\n\n```js\n//App.js\nimport React from 'react';\n\nfunction App() {\n  return (\n    \u003cdiv className=\"App\"\u003e\n      hellowrold!\n    \u003c/div\u003e\n  );\n}\nexport default App;\n\n//index.js\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport App from './App';\n\n\nReactDOM.render(\u003cApp /\u003e, document.getElementById('root'));\n```\n\n​\t浏览器中输入localhost:3000看到如下结果表示运行成功：\t\n\n![](./assets/react空工程运行结果.png)\n\n## 六.合约部署测试\n\n### **安装solc编译器**\n\n```sh\n//执行该命令必须与package.json位于同一文件夹下\ncd lottery\nnpm install solc@0.4.25 --save\n```\n\n### **安装web3**\n\n```sh\n//执行该命令必须与package.json位于同一文件夹下\ncd lottery\nnode install web3@1.2.1 --save\n```\n\n### **启动命令行版本Ganache**\n\n```sh\n//如果没有命令行版本Ganache需要先进行安装\nnpm install ganache-cli -g\n//启动巧克力\nganache-cli\n```\n\n​\t启动之后如果出现如下情况表示Gananche安装成功：![](./assets/巧克力启动.png)\n\n### 使用指定方式启动Ganache\n\n​\t每次输入ganache-cli启动后，会有如下特点：\n\n- ​\t\t默认的服务为：Listening on 127.0.0.1:8545。\n- ​\t\t每次启动时，10个地址是变化的，不太方便。\n\n​\t在这里我们使用参数指定启动的数据:\n\n​\t\tgenache-cli  -m    指定“助记词”，\n\n​\t\t\t\t\t-h     指定“ip”，\n\n​\t\t\t\t\t-p      “port”。\n\n#### 获取助记词的方法\n\n```\nganache-cli \n```\n\n​     \t启动后可以看到如下界面：![](./assets/获取Ganache的助记词.png)\n\n如图中所示，Mnemonic即为助记词，拷贝这些助记词；\n\n#### 通过助记词启动ganache-cli\n\n```sh\nganache-cli -h 127.0.0.1 -p 7545 -m \"caught flower unique despair swift convince tip royal alter tuition token marble\"\n```\n\n#### 导入助记词到MetaMask\n\n​\t启动MetaMask,选择从助记词还原\n\n![](./assets/netmask设置1.png)\n\n​\t\t将助记词拷贝至wallet seed并设置新密码\n\n​\t\t![](./assets/netmask设置2.png)\n\n此时MetaMask已经连接到ganache-cli，可以通过MetaMask对ganache-cli中的账户进行转账或查看操作，从而模拟用户的真实操作。\n\n### 合约编译部署\n\n在lottery文件夹下创建compile.js，并编写代码如下：\n\n```js\nlet fs = require('fs')\nlet solc = require('solc')\n\n\n//指定utf-8，返回的就是一个string字符串，下面编译的时候，就不需要使用toString()方法\n//如果不指定utf-8，返回的就是Buffer, 那么下面编译时，需要使用toString()方法\nlet contractInfo = fs.readFileSync('./lottery.sol', 'utf-8')\n\nlet compiledInfo = solc.compile(contractInfo, 1)\nconsole.log(compiledInfo)\n\n//JSON.parse(字符串) =\u003e 转换成一个json对象\n// JSON.stringfy(json对象) =\u003e 转换成一个string\n\nlet res = fs.writeFileSync('./compileInfo.json', JSON.stringify(compiledInfo), 'utf-8')\n\n\nmodule.exports = compiledInfo['contracts'][':Lottery']\n\n```\n\n在lottery文件夹下创建deploy.js，并编写代码如下：\n\n```js\nlet {\n    bytecode,\n    interface\n} = require('./compile')\n\nlet Web3 = require('web3')\n\nconsole.log('bytecode :', bytecode)\nconsole.log('interface :', interface)\n\n//初始化web3\n//ganache-cli本地测试环境，不需要指定助记词就可以使用里面的账户\nlet web3 = new Web3('http://127.0.0.1:7545')\n\nlet deploy = async () =\u003e {\n    let accounts = await web3.eth.getAccounts()\n\n    console.log('accounts : ', accounts)\n\n    //填写abi, 第二参数是合约地址，部署的时候不用填写\n    let contract = new web3.eth.Contract(JSON.parse(interface))\n\n    //填写bytecode和构造函数参数\n    contract.deploy({\n        data: bytecode,\n        // arguments:[], //如果没有构造函数参数，可以不写这个字段\n    }).send({\n        from: accounts[0],\n        // to :  //部署合约时，不需要填写to字段\n        value: 0,\n        // gas : '8000000000', 如果太小，创建交易时gas不足，会失败。如果过多，会提示超出gas上限，最多800万gas\n        gas: '800000'\n    }).then(res =\u003e {\n        console.log('新部署合约的地址为: ', res.options.address)\n    }).catch(err =\u003e {\n        console.log('部署合约失败:', err)\n    })\n}\n\ndeploy()\n\n```\n\n执行如下命令：\n\n```shell\nnode compile.js\nnode deploy.js\n\n```\n\n![](./assets/合约的编译部署.png)\n\n得到上述地址表示合约部署成功。\n\n## 七.项目模块设计与实现\n\n​\t根据项目框架图可知，项目整体采用MVC架构，其中controller层使用react工程初始化的App.js，Model层和View层需要另外创建。\n\n​\t在lottery/src文件夹下创建eth文件夹和display文件夹；\n\n```sh\ncd ./lottery/src\nmkdir eth    //model层代码\nmkdir display//view层代码\n\n```\n\n### 获取合约实例\n\n```sh\ncd ./lottery/src/eth\ntouch getInstance.js\n\n```\n\n​\t获取合约实例需要通过ABI和部署地址，ABI可以通过我们执行compile.js中的compile.json获取，也可以通过remix在线编译器获取；\n\n​\t![](./assets/BI的获取.png)\n\n地址可以通过编译deploy.js后的地址获取。\n\ngetInstance.js代码如下：\n\n```js\n//根据ABI和部署地址获得合约实例\nconst ABI = [ { \"constant\": false, \"inputs\": [], \"name\": \"draw\", \"outputs\": [], \"payable\": false, \"stateMutability\": \"nonpayable\", \"type\": \"function\" }, { \"constant\": true, \"inputs\": [], \"name\": \"getBalance\", \"outputs\": [ { \"name\": \"\", \"type\": \"uint256\" } ], \"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\": \"manager\", \"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\": false, \"inputs\": [], \"name\": \"play\", \"outputs\": [], \"payable\": true, \"stateMutability\": \"payable\", \"type\": \"function\" }, { \"constant\": true, \"inputs\": [], \"name\": \"winner\", \"outputs\": [ { \"name\": \"\", \"type\": \"address\" } ], \"payable\": false, \"stateMutability\": \"view\", \"type\": \"function\" }, { \"constant\": false, \"inputs\": [], \"name\": \"undraw\", \"outputs\": [], \"payable\": false, \"stateMutability\": \"nonpayable\", \"type\": \"function\" }, { \"constant\": true, \"inputs\": [ { \"name\": \"\", \"type\": \"uint256\" } ], \"name\": \"players\", \"outputs\": [ { \"name\": \"\", \"type\": \"address\" } ], \"payable\": false, \"stateMutability\": \"view\", \"type\": \"function\" }, { \"inputs\": [], \"payable\": false, \"stateMutability\": \"nonpayable\", \"type\": \"constructor\" } ]\nconst Address = '0x2F1171a6F34ad4407e362C0F801D7883104D7FDa';\n\n//实例化web3\nlet Web3 = require('web3');\nlet web3 = new Web3('http://127.0.0.1:7545');\n\n//获取合约实例\nlet lotteryIntance = new web3.eth.Contract(ABI,Address);\n//将合约实例导出，这样在其他地方就可以直接与合约交互\n//如果只导出一个对象，可以使用default修饰，这样在导入时可以定义一个其他的名字\nexport default lotteryIntance\n\n\n```\n\n改写App.js代码如下：\n\n```js\nimport React,{Component} from 'react';\nimport instance from './eth/getInstance'\n\nclass App extends Component{\n    //生命周期函数，在页面挂载的时候自动执行\n    async componentDidMount(){\n        let manager = await instance.methods.manager().call()\n        console.log('管理员地址：',manager);\n    }\n    render(){\n        return (\n            \u003cdiv className=\"App\"\u003e\n                hellowrold!\n            \u003c/div\u003e\n        );\n    }\n}\nexport default App;\n\n\n```\n\n​\t打开浏览器，访问localhost:3000,按下F12，如果看到控制台输出管理员地址则表示调用成功。![](./ssets/取合约实例测试返回结果.png)\n\n此时表明我们已经可以通过页面获取到合约实例；\n\n##### state状态变量\n\n​\tstate：内置状态变量，在类内进行数据传递；\n\n```js\nState={\ta:'',\t}\n\n读操作：let a = this.state.a;\n\n写操作：this.setState({a:'hello'});\n\n```\n\n**react中标签内的变量需要使用{}包裹起来，否则会当做字符串显示**\n\n继续改写App.js如下：\n\n```js\nimport React,{Component} from 'react';\nimport instance from './eth/getInstance'\n\nclass App extends Component{\n    //状态变量用于传递数据\n    state={\n        manager:'',\n        round:0,\n        winner:'',\n        allPlayers:[],\n        balance:0,\n    }\n    //生命周期函数，在页面挂在的时候自动执行\n    async componentDidMount(){\n        //管理员地址：manager\n        let manager = await instance.methods.manager().call();\n        //当前的期数：round\n        let round = await instance.methods.round().call();\n        //上一期中奖者地址：winner\n        let winner = await instance.methods.winner().call();\n        //玩家数组：players\n        let allPlayers = await instance.methods.getPlayers().call();\n        //合约里边的金额\n        let balance = await instance.methods.getBalance().call();\n        //打印上述数据\n        let detailInfo ={manager,round,winner,balance};\n        console.table(detailInfo);\n        //设置状态变量\n        this.setState({manager,round,winner,allPlayers,balance});\n\n    }\n    render(){\n        let {manager,round,winner,allPlayers,balance} = this.state\n        return (\n\n            \u003cdiv className=\"App\"\u003e\n                \u003cp\u003ehellowrold!\u003c/p\u003e\n                \u003cp\u003emanager:{manager}\u003c/p\u003e\n                \u003cp\u003eround:{round}\u003c/p\u003e\n                \u003cp\u003ewinner:{winner}\u003c/p\u003e\n                \u003cp\u003eallPlayers:{allPlayers}\u003c/p\u003e\n                \u003cp\u003ebalance:{balance}\u003c/p\u003e\n            \u003c/div\u003e\n        );\n    }\n}\n\n\nexport default App;\n\n\n```\n\n此时运行结果如下图所示：![](./ssets/约实例页面展示.png)\n\n### 页面UI设计与实现\n\n​\t这里简单起见，我们不再手写代码实现UI而是，直接从semantic-ui上拉取一个页面下来。\n\n```sh\n//安装semanct-ui-react\nnpm install semantic-ui-react --save\n//安装semanct-ui-css\t\nnpm install semantic-ui-css --save\n\n```\n\n​\t浏览器内输入网址：https://react.semantic-ui.com/views/card/#types-card\n\n​\t点击try-it如下图所示![](./assets/semantic.png)\n\n之后下方会出现一段代码，拷贝这段代码，粘贴到display.js下；\n\n```sh\nvim ./lottery/src/display/display.js\n\n```\n\n在App.js中引入display.js如下图所示：![](./assets/CardExample引入.png)\n\n![](./assets/引入2.png)\n\n在index.js中引入semantic-ui-css,如下图所示：\n\n![](./assets/css样式的引入.png)\n\n在./lottery/public文件夹下放置彩票logo图片，并修改display.js中的图片放置地址。**注意：图片资源地址的根目录为/public**![](./assets/修改图片放置代码.png)\n\n最后效果如下图所示：\n\n![](./assets/UI设计后部署效果.png)\n\n##### 修改页面代码\n\n修改display.js代码如下：\n\n```js\nimport React from 'react'\nimport { Card, Icon, Image, Statistic } from 'semantic-ui-react'\n\nconst CardExampleCard = () =\u003e (\n    \u003cCard\u003e\n        \u003cImage src='/logo.jpg' wrapped ui={false} /\u003e\n        \u003cCard.Content\u003e\n            \u003cCard.Header\u003e皇家彩票\u003c/Card.Header\u003e\n            \u003cCard.Meta\u003e开奖员地址:\u003c/Card.Meta\u003e\n            \u003cp\u003e0x1234567890\u003c/p\u003e\n            \u003cCard.Meta\u003e当前地址:\u003c/Card.Meta\u003e\n            \u003cp\u003e0x1234567890\u003c/p\u003e\n            \u003cCard.Description\u003e全天24小时在线,每周一、周二、周六晚八点开奖\u003c/Card.Description\u003e\n        \u003c/Card.Content\u003e\n        \u003cCard.Content extra\u003e\n            \u003ca\u003e\n                \u003cIcon name='user' /\u003e\n                10人\n            \u003c/a\u003e\n        \u003c/Card.Content\u003e\n        \u003cCard.Content extra\u003e\n            \u003cStatistic color=\"red\"\u003e\n                \u003cStatistic.Value\u003e10 ETH\u003c/Statistic.Value\u003e\n                \u003cStatistic.Label\u003e奖金池\u003c/Statistic.Label\u003e\n            \u003c/Statistic\u003e\n        \u003c/Card.Content\u003e\n        \u003cCard.Content extra\u003e\n            \u003cStatistic color=\"blue\"\u003e\n                \u003cStatistic.Value\u003e第0 期\u003c/Statistic.Value\u003e\n                \u003cStatistic.Label\u003e期数\u003c/Statistic.Label\u003e\n                \u003ca\u003e点击我查看交易历史\u003c/a\u003e\n            \u003c/Statistic\u003e\n        \u003c/Card.Content\u003e\n    \u003c/Card\u003e\n)\n\nexport default CardExampleCard\n\n```\n\n### 页面数据导入\n\n​\tprops：负责多个组件之间的数据传递（包括：数据，函数）;\n\n​\t获取数据涉及到不同类之间的数据传递，此时需要使用props；\n\n​\tgetInstance.js代码改写如下：![](./assets/web3代码改写.png)\n\n​\tApp.js代码改写如下：\n\n```js\nimport React,{Component} from 'react';\nimport {instance,web3} from './eth/getInstance'\nimport CardExampleCard from './display/display'\nclass App extends Component{\n    //状态变量用于传递数据\n    state={\n        manager:'',\n        round:0,\n        winner:'',\n        allPlayers:[],\n        balance:0,\n        currentAccount:'',\n    }\n    //生命周期函数，在页面挂在的时候自动执行\n    async componentDidMount(){\n        //获取所有账户地址\n        let accounts = await web3.eth.getAccounts();\n        //获取当前账户地址\n        let currentAccount = accounts[1];\n        //管理员地址：manager\n        let manager = await instance.methods.manager().call();\n        //当前的期数：round\n        let round = await instance.methods.round().call();\n        //上一期中奖者地址：winner\n        let winner = await instance.methods.winner().call();\n        //玩家数组：players\n        let allPlayers = await instance.methods.getPlayers().call();\n        //合约里边的金额\n        let balance = await instance.methods.getBalance().call();\n        //打印上述数据\n        let detailInfo ={manager,round,winner,balance};\n        console.table(detailInfo);\n        //设置状态变量\n        this.setState({manager,round,winner,allPlayers,balance,currentAccount});\n\n    }\n    render(){\n        let {manager,round,winner,allPlayers,balance,currentAccount} = this.state\n        return (\n\n            \u003cdiv className=\"App\"\u003e\n                \u003cCardExampleCard allData={this.state}/\u003e\n            \u003c/div\u003e\n        );\n    }\n}\n\n\nexport default App;\n\n\n```\n\n​\tdisplay.js代码改写如下：\n\n```js\nimport React from 'react'\nimport { Card, Icon, Image, Statistic } from 'semantic-ui-react'\n\nconst CardExampleCard = (props) =\u003e {\n    let allData = props.allData;\n    let{manager,round,winner,allPlayers,balance,currentAccount}=allData;\n    return(\n    \u003cCard\u003e\n        \u003cImage src='/logo.jpg' wrapped ui={false} /\u003e\n        \u003cCard.Content\u003e\n            \u003cCard.Header\u003e皇家彩票\u003c/Card.Header\u003e\n            \u003cCard.Meta\u003e开奖员地址:\u003c/Card.Meta\u003e\n            \u003cp\u003e{manager}\u003c/p\u003e\n            \u003cCard.Meta\u003e当前地址:\u003c/Card.Meta\u003e\n            \u003cp\u003e{currentAccount}\u003c/p\u003e\n            \u003cCard.Meta\u003e上一期中奖地址:\u003c/Card.Meta\u003e\n            \u003cp\u003e{winner}\u003c/p\u003e\n            \u003cCard.Description\u003e全天24小时在线,每周一、周二、周六晚八点开奖\u003c/Card.Description\u003e\n        \u003c/Card.Content\u003e\n        \u003cCard.Content extra\u003e\n            \u003ca\u003e\n                \u003cIcon name='user' /\u003e\n                    {allPlayers.length}人\n            \u003c/a\u003e\n        \u003c/Card.Content\u003e\n        \u003cCard.Content extra\u003e\n            \u003cStatistic color=\"red\"\u003e\n                \u003cStatistic.Value\u003e{balance} ETH\u003c/Statistic.Value\u003e\n                \u003cStatistic.Label\u003e奖金池\u003c/Statistic.Label\u003e\n            \u003c/Statistic\u003e\n        \u003c/Card.Content\u003e\n        \u003cCard.Content extra\u003e\n            \u003cStatistic color=\"blue\"\u003e\n                \u003cStatistic.Value\u003e第{round} 期\u003c/Statistic.Value\u003e\n                \u003cStatistic.Label\u003e期数\u003c/Statistic.Label\u003e\n                \u003ca\u003e点击我查看交易历史\u003c/a\u003e\n            \u003c/Statistic\u003e\n        \u003c/Card.Content\u003e\n    \u003c/Card\u003e\n);}\n\nexport default CardExampleCard\n\n```\n\n#### 添加按钮\n\n​\t 页面需要三个按钮，分别是开奖、退奖、投注；\n\nApp.js页面修改如下：\n\n​\t\t![](./assets/添加按钮.png)\n\n##### 按钮业务逻辑的实现\n\n​\t在App.js中添加如下代码：\n\n```js\n //调用的函数\n    play=async()=\u003e {\n        try{\n            await instance.methods.play().send({\n                from:this.state.currentAccount,\n                value:(1*10)**18\n            });\n            alert('投注成功');\n            window.location.reload(true);\n        }catch{\n            alert('投注失败');\n            window.location.reload(true);\n        }\n\n    }\n    draw=async()=\u003e{\n        try{\n            await instance.methods.undraw().send({\n                from:this.state.currentAccount,\n            });\n            //在这里重新设置中将着\n            let winner = await instance.methods.winner().call();\n            console.log('中奖地址：',winner);\n            alert('开奖成功');\n            window.location.reload(true);\n        }catch{\n            alert('开奖失败');\n            window.location.reload(true);\n        }\n    }\n    undraw=async()=\u003e{\n        try{\n            await instance.methods.undraw().send({\n                from:this.state.currentAccount,\n            });\n            alert('退奖成功');\n            window.location.reload(true);\n        }catch{\n            alert('退奖失败');\n            window.location.reload(true);\n        }\n    }\n    render(){\n        let {manager,round,winner,allPlayers,balance,currentAccount} = this.state\n        return (\n\n            \u003cdiv className=\"App\"\u003e\n                \u003cCardExampleCard\n                    allData={this.state}\n                    play={this.play}\n                    draw={this.draw}\n                    undraw={this.undraw}\n                /\u003e\n            \u003c/div\u003e\n        );\n    }\n\n```\n\n### 链接MetaMask\n\n​\t当前项目中，我们的用户是固定的，每次取Ganace中account[1]的地址，在实际业务中，这里需要使用用户自己的provider。\n\n​\t在安装MetaMask后，MetaMask会在浏览器中注册一个自己的web3实例，MetaMask在链接网络之后，会生成一个自己的web3 provider。\n\n​\t修改getInstance.js如下：![](./assets/使用用户自己的web3.png)\n\n​\t\t\n\n## 八.项目优化\n\n### 单位优化\n\n​\t在运行过程中，我们发现投注的单位是wei，但是页面显示要求的单位是ether，会出现如下问题：\n\n​\t![](./assets/单位优化.png)\n\n修改App.js代码如下：![](./assets/单位转换.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fminecoinchain%2Flotterybyeth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fminecoinchain%2Flotterybyeth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fminecoinchain%2Flotterybyeth/lists"}