使用webpack开发以太坊的前端应用

  1. 1. 前情提要
  2. 2. 游戏步骤
  3. 3. boilerplate
  4. 4. 添加前端逻辑
  5. 5. 修改UI
  6. 6. 编译,部署,执行
  7. 7. 最后的话

前情提要

使用Truffle开发以太坊智能合约一文中,我们建立了一个最基本的剪刀石头布的以太坊智能合约,并部署到了local的testnet上。

排除这个合约本身的不安全的问题,考虑到用户不可能在truffle的console上敲js代码来执行调用合约,我们还得得建立简单方便的前端应用。

游戏步骤

首先是完成后的简陋的UI的截图。

游戏时候,使用metamask连上local的testnet。然后自己扮演两个角色切换账户即可。

首先两个账户分别转账5wei进行注册。

然后两个用户分别选择是出石头,剪刀,还是布。

最后赢家被记录到LasterWinner这个项目里面。赢家还将获得对方的5wei。

boilerplate

建立一个新的目录。Truffle提供了很方便的已经成型的boilerplate来建立前端的应用。

1
2
3
$ mkdir rps-frontend
$ cd rps-frontend
$ truffle unbox webpack

一阵各种download之后,我们发现当前目录下多出来很多东西。
webpack的boilerplate是一个简单的基于ETH的Token的例子。
其中最主要的是app/这个文件。

1
2
3
4
5
6
app
├── index.html
├── javascripts
│   └── app.js
└── stylesheets
└── app.css

里面的内容由于不是我们需要部署的协议的内容,所以基本上我们都要移除。

首先删掉contracts里面除了Migration.sol以外的所有内容,然后再用这里的文件替换migrations里面的2_deploy_contracts.js的内容。这样理论上我们就可以部署我们的石头剪刀布协议了。

添加前端逻辑

打开app/javascripts/app.js进行修改。
具体可以参考这里, 我们只提几个注意点。

1
2
3
4
5
6
7
8
import "../stylesheets/app.css";

import { default as Web3} from 'web3'; // 用来与eth的节点交互的lib。这里我们需要用这个库来获取用户信息。
import { default as contract } from 'truffle-contract'

import rps_artifacts from '../../build/contracts/rps.json' // 协议的ABI接口。这个是通过truffle migrate之后生成的。

var Rps= contract(rps_artifacts);

也许我们已经习惯使用MetaMask来和基于Ethereum的网页应用交互。下面则是获取默认用户账号信息的代码,已经由这个boilderplate自动生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
web3.eth.getAccounts(function(err, accs) {
if (err != null) {
alert("There was an error fetching your accounts.");
return;
}

if (accs.length == 0) {
alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly.");
return;
}

accounts = accs;
account = accounts[0];

self.refreshWinner();
});

接下来是通过API查询blockchain获取最后赢家的函数。核心的是rps.getLastWinner.call({from: account}), 这和我们在truffle的console的输入一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
refreshWinner: function() {
var self = this;
var rps;
Rps.deployed().then(function(instance) {
rps = instance;
return rps.getLastWinner.call({from: account}); // 注意这里
}).then(function(value) {
var balance_element = document.getElementById("winner");
balance_element.innerHTML = value.valueOf();
}).catch(function(e) {
console.log(e);
self.setStatus("Error getting balance; see log.");
});
},

再然后是注册用户并转5wei的代码。同样核心是rps.register({value: amount, from: account})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

register: function() {
var self = this;
var amount = parseInt(document.getElementById("amount").value);
this.setStatus("Initiating transaction... (please wait)");
var rps;
Rps.deployed().then(function(instance) {
rps= instance;
return rps.register({value: amount, from: account});
}).then(function() {
self.setStatus("Transaction complete!");
self.refreshWinner();
}).catch(function(e) {
console.log(e);
self.setStatus("Error sending coin; see log.");
});
},

最后是玩家执行游戏进行选择的代码。重点还是rps.play(selection, {from: account})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
play: function() {
var self = this;
var selection = document.getElementById("selection").value;
var rps;
Rps.deployed().then(function(instance) {
rps= instance;
return rps.play(selection, {from: account}); //重点
}).then(function() {
self.setStatus("Transaction complete!");
self.refreshWinner();
}).catch(function(e) {
console.log(e);
self.setStatus("Error sending coin; see log.");
});

}

修改UI

接下来就是修改默认的boilerplate的index.html了。
我们得添加上显示最后赢家,输入转账数目,确认注册,选择出拳内容,确认出拳内容的一些东西。
由于boilerplate本来写得比较脏,我也只是适当修改,所以请自己参考下面完整的repo吧。

编译,部署,执行

最后就是打开truffle dev 然后依次compile,migrate --reset了。truffle dev会自动建立local的testrpc的testnet,所以这个时候npm run dev,打开浏览器就可以看到我们上面截图中的应用内容了。

最后的话

使用MetaMask大大方便了用户通过webapplication来执行智能合约。也使得我们可以顾虑很少地开发更加健全的智能合约。试想一下,如果没有MetaMask,你的智能合约就有可能需要用户去输入自己的private key。那你的应用大抵也就没人用了。

这里是整个完整的项目。

以上。

如果你觉得本文对你有帮助,请给我点赞助。