前言

最近DAPP的开发貌似很火,学习了区块链的一些知识之后,相信有很多人和我一样,也想了解开发一个DAPP是一个怎样的流程。

下面将通过一个简单的栗子来初识一下DAPP的开发流程,届时,我们也将开发出第一个DAPP应用–《永存的留言》。

在线体验(Ludis):http://words.ldsun.com/ <http://words.ldsun.com/>。

项目介绍

《永存的留言》是一个基于以太坊的在线留言平台。它的功能十分简单–用户可以在平台上进行留言,平台每10s随机的展示留言内容。
但是它的特点在于,利用区块链的特性,保证了数据的真实性、完整性和安全性。


* 使用Solidity开发后端方法
* 使用Truffle框架
* 基于unbox react脚手架项目 <http://truffleframework.com/boxes/>
* 部署在以太坊测试网络上Ropoetn Test Network
* 使用MetaMask钱包插件发布交易
开发步骤

下载react项目模板

确保本地已经准备好Truffle所需的环境,准备以下命令,下载react项目模板。
$ mkdir a && cd a
truffle unbox react
当看到 Unbox successful. Sweet!提示时,表明下载成功。

编写智能合约

这是我们唯一的实现合约,包含的发送留言和读取留言的方法。
pragma solidity ^0.4.19; contract SimpleStorage { // 留言结构体 struct Message {
string word;// 留言 address from; // 留言者地址 string timestamp ; // 留言unix时间戳 }
Message[] private wordArr;/** * 写入留言的方法 */ function setWord(string s, string t)
public { wordArr.push(Message({ word: s, from: msg.sender, timestamp: t })); }
/** * 获取随机留言的方法 */ function getRandomWord(uint random) public view returns
(uint, string, address, string) { if(wordArr.length==0){ return (0, "",
msg.sender,""); }else{ Message storage result = wordArr[random]; return
(wordArr.length, result.word, result.from, result.timestamp); } } }
编译、部署合约

修改发布的脚本。
var SimpleStorage = artifacts.require("./SimpleStorage.sol"); //var Words =
artifacts.require("Words"); module.exports = function(deployer) {
deployer.deploy(SimpleStorage);//deployer.deploy(Words); };
执行truffle compile进行合约的编译。

获取合约地址。

* 这里我们打开MetaMask钱包插件,左上角选择Ropoetn Test Network网络.

* 利用Remix编译,发布合约到以太坊测试环境。

* 通过MetaMask的交易hash查询,获取已经部署到以太坊测试网络中的合约地址。

编写前端页面

这个部分主要是编写前端的展示效果和与合约交互的逻辑,这一部分最难编写,也最耗时间。

* 主要逻辑代码 const contractAddress = "0x39e5196750dcddb1aaf6dda7d6e8dbb633482905"
// 合约地址(以太坊测试网络) var simpleStorageInstance // 合约实例 class App extends Component {
// 初始化构造 constructor(props) { super(props) this.state = { word: null, from: null
, timestamp:null, random: 0, count: 0, input: '', web3: null, emptyTip:
"还没有留言,快来创建全世界第一条留言吧~", firstTimeLoad: true, loading: false, loadingTip:
"留言正在写入,请耐心等待~", waitingTip: "留言正在写入,请耐心等待~", successTip: "留言成功", animate: "",
in: css(styles.in), out: css(styles.out) } } // 获取Web3实例 componentWillMount() {
// Get network provider and web3 instance. getWeb3 .then(results => { this
.setState({ web3: results.web3 })// Instantiate contract once web3 provided.
this.instantiateContract() }) .catch(() => { console.log('Error finding web3.')
}) }// 获取合约实例 instantiateContract() { /* * SMART CONTRACT EXAMPLE * * Normally
these functions would be called in the context of a * state management library,
but for convenience I've placed them here. */ const contract = require(
'truffle-contract') const simpleStorage = contract(SimpleStorageContract)
simpleStorage.setProvider(this.state.web3.currentProvider) // Get accounts. this
.state.web3.eth.getAccounts((error, accounts) => {
simpleStorage.at(contractAddress).then(instance => { simpleStorageInstance =
instance/*simpleStorage.deployed().then((instance) => { simpleStorageInstance =
instance // 部署本地Ganache*/ console.log("合约实例获取成功") }) .then(result => { return
simpleStorageInstance.getRandomWord(this.state.random) }) .then(result => {
console.log("读取成功", result) if(result[1]!=this.setState.word){ this.setState({
animate:this.state.out }) setTimeout(() => { this.setState({ count: result[0].c[
0], word: result[1], from: result[2], timestamp: result[3], animate: this
.state.in, firstTimeLoad:false }) }, 2000) }else{ this.setState({ firstTimeLoad:
false }) } this.randerWord() }) }) } // 循环从区块上随机读取留言 randerWord() {
setInterval(() => {let random_num = Math.random() * (this.state.count? this
.state.count:0) this.setState({ random: parseInt(random_num) }) console.log(
"setInterval读取", this.state.random) simpleStorageInstance.getRandomWord(this
.state.random) .then(result => { console.log("setInterval读取成功", result) if
(result[1]!=this.setState.word){ this.setState({ animate: this.state.out })
setTimeout(() => {this.setState({ count: result[0].c[0], word: result[1], from:
result[2], timestamp: result[3], animate: this.state.in }) }, 2000) } }) },
10000) } // 写入区块链 setWord(){ if(!this.state.input) return this.setState({
loading:true }) let timestamp = new Date().getTime()
simpleStorageInstance.setWord(this.state.input, String(timestamp), {from: this
.state.web3.eth.accounts[0]}) .then(result => { this.setState({ loadingTip: this
.state.successTip }) setTimeout(() => {this.setState({ loading: false, input: ''
, loadingTip:this.state.waitingTip }) }, 1500) }) .catch(e => { // 拒绝支付 this
.setState({ loading:false }) }) } // 时间戳转义 formatTime(timestamp) { let date =
new Date(Number(timestamp)) let year = date.getFullYear() let month =
date.getMonth() +1 let day = date.getDate() let hour = date.getHours() let
minute = date.getMinutes()let second = date.getSeconds() let fDate = [year,
month, day, ].map(this.formatNumber) return fDate[0] + '年' + fDate[1] + '月' +
fDate[2] + '日' + ' ' + [hour, minute, second].map(this.formatNumber).join(':') }
/** 小于10的数字前面加0 */ formatNumber(n) { n = n.toString() return n[1] ? n : '0' + n
} }
运行项目

使用npm start启动项目,浏览器的3000端口运行。
效果如下(一定要连接到Ropoetn Test Network网络才可以)。


总结

这样我们就开发出了我们的第一个DAPP,体会了开发的基本流程。

* 明确项目需求
* 确定开发环境
* 编写、测试、部署合约
* 设计前端页面,使前后端交互
* 发布测试
项目源码

GitHub <https://github.com/mokeychan/etherumForeverWords>

参考文章

Ludis的博文
<https://www.ldsun.com/2018/03/04/yi-ge-wan-zheng-de-zhi-neng-he-yue-qu-kuai-lian-shang-yong-cun-de-liu-yan/>

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信