下面的部分软件需要翻墙。这里默认你已经会科学上网,不懂的可以自己搜索解决。

软件安装

chrome浏览器

下载地址

metamask插件

谷歌应用商店中搜索metamask

ganche(图形版)

下载地址

nodejs和npm

下载地址

安装完成需要将node.exe所在路径加入环境变量PATH中

并且需要保证在cmd中可以使用nodenpm命令,简单测试:

$ node -v
$ npm -v

开发环境配置

  1. 启动ganache
  2. remix切换运行环境。点击remix在线编辑器右侧的run->Environment,选择Injected Web3
  3. metamask连接ganache。打开chrome浏览器中的metamask插件,首先通过12个单词短语恢复账号。然后通过"Custom RPC", 输入http://localhost:7545,保存并将换网络切换至http://localhost:7545,连接成功后显示主账户信息''

配置截图

truffle init 从零开始创建简单DApp项目-LMLPHP

truffle init 从零开始创建简单DApp项目-LMLPHP

truffle init 从零开始创建简单DApp项目-LMLPHP

truffle init 从零开始创建简单DApp项目-LMLPHP

Demo项目

$ mkdir demo
$ cd demo
$ npm init

后面全部enter默认选择(直接敲回车)就好了,效果如下:

$ ls
package.json
$ cat package.json
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
$ npm install ethereum/web3.js --save

安装比较耗时, 需要耐心等待安装完成

完成后效果:

$ ls
node_modules/ package.json package-lock.json $ ls node_modules/
bignumber.js/ crypto-js/ web3/ xmlhttprequest/
cookiejar/ utf8/ xhr2-cookies/ $ ls node_modules/web3/
bower.json example/ lib/ package.json styleguide.md
circle.yml gulpfile.js* LICENSE.md package-init.js
dist/ index.js package.js README.md # 发现配置已经发生了改变
$ cat package.json
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"web3": "github:ethereum/web3.js"
}
}

完整项目结构

在项目根目录下创建index.htmlmain.css

如下:

$ ls demo
index.html main.css node_modules/ package.json package-lock.json

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DApp Demo</title>
<link rel="stylesheet" type="text/css" href="main.css">
<script src="./node_modules/web3/dist/web3.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
</head>
<body>
<div class="container">
<h1>Simple DApp Demo</h1>
<h2 id="info"></h2> <label for="name" class="col-lg-2 control-label">Name</label>
<input id="name" type="text">
<label for="age" class="col-lg-2 control-label">Age</label>
<input id="age" type="text"> <button id="button">更新个人信息</button>
</div>
<script>
window.addEventListener('load', function () {
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:7545"));
}
//web3.eth.defaultAccount = web3.eth.accounts[0]; // replace with your abi code
const abi = [
{
"constant": false,
"inputs": [
{
"name": "_name",
"type": "string"
},
{
"name": "_age",
"type": "uint256"
}
],
"name": "setPersonalInfo",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getPersonalInfo",
"outputs": [
{
"name": "",
"type": "string"
},
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]; // replace with your contract address
const address = "0x0d4aed2bf6178c870355ce1100a11e8fafdbd15d"; // create contract instance
const PersonalInfo = web3.eth.contract(abi).at(address);
console.log("PersonalInfoContract:", PersonalInfo); PersonalInfo.getPersonalInfo(function (error, result) {
if (!error) {
$("#info").html(result[0] + ' (' + result[1] + ' years old)');
console.log("get PersonalInfo success: ", result);
} else {
console.error("failed to get PersonalInfo :", error);
}
}); $("#button").click(function () {
var name = $("#name").val();
var age = $("#age").val();
PersonalInfo.setPersonalInfo(name, age, function (error, result) {
if (!error) {
// update label
$("#info").html(name + ' (' + age + ' years old)');
console.log("setPersonalInfo success: " + result);
} else {
consoe.log("fail to setPersonalInfo: " + error);
}
});
});
});
</script>
</body>
</html>

main.css

body {
background-color:#F0F0F0;
padding: 2em;
font-family: 'Raleway','Source Sans Pro', 'Arial';
}
.container {
width: 50%;
margin: 0 auto;
}
label {
display:block;
margin-bottom:10px;
}
input {
padding:10px;
width: 50%;
margin-bottom: 1em;
}
button {
margin: 2em 0;
padding: 1em 4em;
display:block;
} #info {
padding:1em;
background-color:#fff;
margin: 1em 0;
}

使用web3.js和智能合约进行交互

使用solidity在线编辑器remix

solidity代码如下:

pragma solidity ^0.4.16;

contract PersonalInfo {
string name;
uint age;
function setPersonalInfo(string _name, uint _age) public {
name =_name;
age = _age;
}
function getPersonalInfo() public view returns(string, uint){
return (name, age);
}
}

使用metamask须知:

如果直接使用浏览器打开index.html会出现跨域访问,如下图:

truffle init 从零开始创建简单DApp项目-LMLPHP

遇到的问题:

The MetaMask Web3 object does not support synchronous methods like eth_sendTransaction without a callback parameter

官方解释:

  • eth_accounts (web3.eth.accounts)
  • eth_coinbase (web3.eth.coinbase)
  • eth_uninstallFilter (web3.eth.uninstallFilter)
  • web3.eth.reset (uninstalls all filters).
  • net_version (web3.version.network).

解决方法:

在合约实例调用对应函数的时候,加上回调函数。形如:

 PersonalInfoContract.setPersonalInfo(name, age, function (error, result) {
if (!error) {
// update label
$("#info").html(name + ' (' + age + ' years old)');
console.log("setPersonalInfo success: " + result);
} else {
consoe.log("fail to setPersonalInfo: " + error);
}
});

参考:

04-16 04:15