首页 > 技术 > 【门罗币】教程:使用 Embark 开发投票 DApp

【门罗币】教程:使用 Embark 开发投票 DApp

摘要:前面我们基于Embark Demo[1]介绍了 Embark 框架,今天使用 Embark 来实实在在开发一个 DApp:从零开发开发一个投票DApp。

前面我们基于Embark Demo[1]介绍了 Embark 框架,今天使用 Embark 来实实在在开发一个 DApp:从零开发开发一个投票DApp。

之前我们也使用Truffle 开发过投票DApp[2],大家可以自行对比两个框架的优劣。

通过本文可以学习到:

1.使用 Embark 创建项目2.利用 EmbarkJS 与合约交互3.Embark 如果部署合约到主网(利用Infura节点)

本文使用的 Embark 版本是 5.2.3

创建Embark项目

>embarknewembark-election

会在当前目录下生成一个 embark-election 目录,并创建好了相应的项目框架文件:如:app/contracts/config/embark.json等。

我们需要在对应的目录中,添加相应的实现。

编写合约

contracts/中添加合约Election.sol:

pragmasolidity^0.6.0;contractElection{//ModelaCandidatestructCandidate{uintid;stringname;uintvoteCount;}mapping(address=>bool)publicvoters;mapping(uint=>Candidate)publiccandidates;//StoreCandidatesCountuintpubliccandidatesCount;//votedeventeventvotedEvent(uintindexed_candidateId);constructor()public{addCandidate("Tiny熊");addCandidate("LearnBlockChain.cn");}functionaddCandidate(stringmemory_name)private{candidatesCount++;candidates[candidatesCount]=Candidate(candidatesCount,_name,0);}functionvote(uint_candidateId)public{//requirethattheyhaven'tvotedbeforerequire(!voters[msg.sender]);//requireavalidcandidaterequire(_candidateId>0&&_candidateId<=candidatesCount);//recordthatvoterhasvotedvoters[msg.sender]=true;//updatecandidatevoteCountcandidates[_candidateId].voteCount++;//triggervotedeventemitvotedEvent(_candidateId);}}

之前有使用过Truffle开发过投票DApp[3],合约的代码完全一样,就不在解释。

Embark 合约编译部署

Embark 合约部署的配置在config/contracts.js, 在deploy字段加入 Election 合约:

 

deploy:{Election:{}}

现在运行embark run, Embark 会自动编译及部署Election.solconfig/blockchain.js配置的development网络。

embark run等价embark run development

blockchain.jsdevelopment网络是使用 ganache-cli 启动的网络,其配置如下:

development:{client:'ganache-cli',clientConfig:{miningMode:'dev'}}

embark启动后,我们可以在 COCKPIT 或 DashBoard 看到Election.sol合约的部署日志,大概类似下面:

deployingElectionwith351122gasatthepriceof1Wei,estimatedcost:351122Wei(txHash:0x9da4dfb951149...d5c306dcabf300a4)Electiondeployedat0x10C257c76Cd3Dc35cA2618c6137658bFD1fFCcfAusing346374gas(txHash:0x9da4dfb951149ea4...d5c306dcabf300a4)finisheddeployingcontracts

编写前端代码

Embark Artifacts

Embark提供了一个 EmbarkJS的JavaScript库,来帮助开发者和合约进行交互。

在使用web3.js 时,和合约交互需要知道合约的ABI及地址来创建JS环境中对应的合约对象,一般代码是这样的:

//需要ABI及地址创建对象varmyContract=newweb3.eth.Contract([...ABI...],'0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe');

Embark 在编译部署后,每个合约会生成一个对应的构件Artifact(可以在embarkArtifacts/contracts/目录下找的这些文件),我们可以直接使用Artifact生成的合约对象调用合约。

一个构件通常会包含:合约的ABI、部署地址、启动代码及其他的配置数据。

查看一下Election.sol对应的构件Election.js代码就更容易理解:

importEmbarkJSfrom'../embarkjs';letElectionJSONConfig={"contract_name":"Election","address":"0x10C257c76Cd3Dc35cA2618c6137658bFD1fFCcfA","code":"...",...,"abiDefinition":[...]};letElection=newEmbarkJS.Blockchain.Contract(ElectionJSONConfig);exportdefaultElection;

Election.js最后一行导出了一个与合约同名的JavaScript 对象,下面看看怎么使用这个对象。

修改前端index.html

在使用embark new embark-election创建项目时, 前端目录app/下生成了一个index.html

 

<html><head><title>Embark</title><linkrel="stylesheet"href="css/app.css"><scriptsrc="js/app.js"></script></head><body><h3>WelcometoEmbark!</h3></body></html>

这里有一个地方需要注意一下,第5 6行引入了css/app.css,js/app.js,而其实app/下并没有这两个文件,这两个文件其实是按照embark.json配置的规程生成的。

embark.json关于前端的配置如下:

 

"app":{"css/app.css":["app/css/**"],"js/app.js":["app/js/index.js"],"images/":["app/images/**"],"index.html":"app/index.html"},

"css/app.css": ["app/css/**"]表示所有在app/css/目录下的文件会被压缩到 dist目录的css/app.cssapp/js/index.js则会编译为js/app.js,其他的配置类似。

我猜测embark 这样统一 css 及 js代码,可能是为了在IPFS之类的去中心化存储上访问起来更方便,在IPFS上传整个目录时,只能以相对路径去访问资源。欢迎留言和我交流。

接下来修改前端部分的代码,主要是在index.htmlbody加入一个table显示候选人,以及加入一个投票框,代码如下(节选):

 

<tableclass="table"><thead><tr><thscope="col">#</th><thscope="col">候选人</th><thscope="col">得票数</th></tr></thead><tbodyid="candidatesResults"></tbody></table><divclass="form-group"><labelfor="candidatesSelect">选择候选人</label><selectclass="form-control"id="candidatesSelect"></select></div>

前端,我们使用了 bootstrap css ,把文件拷贝到app/css目录下,接下来,看看关键的一步:前端如何与与合约交互。

使用 Artifacts与合约交互

EmbarkJS 连接 Web3

创建项目时生成的app/js/index.js生成了如下代码:

 

importEmbarkJSfrom'Embark/EmbarkJS';EmbarkJS.onReady((err)=>{//Youcanexecutecontractcallsaftertheconnection});

这段代码里,EmbarkJS为我们准备了一个onReady回调函数,这是因为EmbarkJS会自动帮我们完成与web3节点的连接与初始化,当这些就绪后(调用onReady),前端就可以和链进行交互了。

大家也许会好奇EmbarkJS怎么知道我们需要连接那个节点呢?其实在config/contracts.js有一个dappConnection配置项:

 

dappConnection:["$EMBARK","$WEB3",//使用浏览器注入的web3,如MetaMask等"ws://localhost:8546","http://localhost:8545"],

$EMBARK: 是Embark在DApp和节点之前实现的一个代理,使用$EMBARK有几个好处:

1.可以在config/blockchain.js配置于DApp交互的账号accounts2.可以更友好的的看到交易记录。

EmbarkJS 会从上到下,依次尝试dappConnection提供的连接,如果有一个可以连接上,就会停止尝试。

获取合约数据渲染界面

当 EmbarkJS 环境准备 onReady后,就可以使用构件Election.js获取合约数据,如获取调用合约获取候选人数量:

 

importEmbarkJSfrom'Embark/EmbarkJS';importElectionfrom'../../embarkArtifacts/contracts/Election.js';EmbarkJS.onReady((err)=>{Election.methods.candidatesCount().call().then(count=>console.log("candidatesCount:"+count););});

代码中直接使用构件导出的Election对象,调用合约方法Election.methods.candidatesCount().call(), 调用合约方法与web3.js 一致。

了解了如何与合约交互,接下来渲染界面就简单了,我们把代码整理下,分别定义3个函数:App.getAccount()App.render()App.onVote()来获取当前账号(需要用来判断哪些账号投过票)、界面渲染、处理点击投标。

 

EmbarkJS.onReady((err)=>{App.getAccount();App.render();App.onVote();});

App.getAccount()的实现如下:

 

import"./jquery.min.js"varApp={account:null,getAccount:function(){web3.eth.getCoinbase(function(err,account){if(err===null){App.account=account;console.log(account);$("#accountAddress").html("YourAccount:"+account);}})},}

在代码中,我们直接使用了web3对象,就是因为EmbarkJS帮我们进行了web3的初始化。另外,我们引入jquery.min.js来进行UI界面的渲染。

App.render()的实现(主干)如下:

 

render:function(){Election.methods.candidatesCount().call().then(candidatesCount=>{varcandidatesResults=$("#candidatesResults");varcandidatesSelect=$('#candidatesSelect');for(vari=1;i<=candidatesCount;i++){Election.methods.candidates(i).call().then(function(candidate){varid=candidate[0];varname=candidate[1];varvoteCount=candidate[2];//RendercandidateResultvarcandidateTemplate="<tr><th>"+id+"</th><td>"+name+"</td><td>"+voteCount+"</td></tr>";candidatesResults.append(candidateTemplate);//RendercandidateballotoptionvarcandidateOption="<optionvalue='"+id+"'>"+name+"</option>";candidatesSelect.append(candidateOption);});}});}

App.onVote()的实现(主干)如下:

 

onVote:function(){$("#vote").click(function(e){varcandidateId=$('#candidatesSelect').val();Election.methods.vote(candidateId).send().then(function(result){App.render();}).catch(function(err){console.error(err);});});}

部署

使用embark run时,会为我们启动一个Geth 或ganache-cli的本地网络部署合约,以及在8000端口上启用一个本地服务器来部署前端应用,我们在浏览器输入http://localhost:8000/就可以看到DApp界面,如图:

当我们的DApp 在测试环境通过后,就可以部署到以太坊的主网。

利用Infura部署到主网

要部署到主网,需要在blockchain.js中添加一个主网网络,这里以测试网Ropsten网络为例:

 

ropsten:{endpoint:"https://ropsten.infura.io/v3/d3fe47c...4f",accounts:[{mnemonic:"你的助记词",hdpath:"m/44'/60'/0'/0/",numAddresses:"1"}]}

如果我们没有自己的主网节点,可以使用 endpoint 来指向以个外部节点,最常用的就是Infura[4]

添加好配置之后,使用build命令来构建主网发布版本:

  •  

embarkbuildropsten#最后是网络参数

所有的文件在生成在dist目录下,把他们部署到线上服务器就完成了部署。也可以使用embark upload ropsten上传到IPFS。

References

[1]Embark Demo:https://learnblockchain.cn/article/566
[2]Truffle 开发过投票DApp:https://learnblockchain.cn/2019/04/10/election-dapp
[3]Truffle开发过投票DApp:https://learnblockchain.cn/2019/04/10/election-dapp
[4]Infura:https://infura.io/

 

免责声明
世链财经作为开放的信息发布平台,所有资讯仅代表作者个人观点,与世链财经无关。如文章、图片、音频或视频出现侵权、违规及其他不当言论,请提供相关材料,发送到:2785592653@qq.com。
风险提示:本站所提供的资讯不代表任何投资暗示。投资有风险,入市须谨慎。
世链粉丝群:提供最新热点新闻,空投糖果、红包等福利,微信:msy2134。