专栏首页Netkiller如何开发以太坊网页钱包·Express + web3.js 实例
原创

如何开发以太坊网页钱包·Express + web3.js 实例

Netkiller Blockchain 手札

本文作者最近在找工作,有意向致电 13113668890

Mr. Neo Chan, 陈景峯(BG7NYT)

中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890 <netkiller@msn.com>

文档始创于2018-02-10

版权 © 2018 Netkiller(Neo Chan). All rights reserved.

版权声明

转载请与作者联系,转载时请务必标明文章原始出处和作者信息及本声明。

内容摘要

这一部关于区块链开发及运维的电子书。

为什么会写区块链电子书?因为2018年是区块链年,区块链是一个风口,前几个风口我都错过了。例如web2.0, 云, 大数据等等,都从身旁擦肩而过。所以我要抓住这次。

这本电子书是否会出版(纸质图书)? 不会,因为互联网技术更迭太快,纸质书籍的内容无法实时更新,一本书动辄百元,很快就成为垃圾,你会发现目前市面的上区块链书籍至少是一年前写的,内容已经过时,很多例子无法正确运行。所以我不会出版,电子书的内容会追逐技术发展,及时跟进软件版本的升级,做到内容最新,至少是主流。

这本电子书与其他区块链书籍有什么不同?市面上大部分区块链书籍都是用2/3去讲区块链原理,只要不到 1/3 的干货,干货不够理论来凑,通篇将理论或是大谈特谈区块链行业,这些内容更多是头脑风暴,展望区块链,均无法落地实施。本书与那些书籍完全不同,不讲理论和原理,面向应用落地,注重例子,均是干货。

写作原则,无法落地的项目作者绝对不会写。凡是写入电子的内容均具备可操作,可落地。

电子书更新频率?每天都会有新内容加入,更新频率最迟不会超过一周,更新内容请关注 https://github.com/netkiller/netkiller.github.io/commits/master

本文采用碎片化写作,原文会不定期更新,请尽量阅读原文 http://www.netkiller.cn/blockchain/index.html

您的打赏是我的写作动力:http://www.netkiller.cn/blockchain/donations.html

接受 ETH 打赏:0x3e827461Cc53ed7c75A29187CfF39629FCAE3661

10.15. Express + web3.js 实现简单网页钱包

下面的例子,实现查询余额,创建账号,ETH转账,代币转账。

10.15.1. 创建项目

安装以太坊环境

curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/gcc/gcc.sh | bash
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/golang/golang-1.10.2.sh | bash
curl -s https://raw.githubusercontent.com/oscm/shell/master/blockchain/ethereum/centos/go-ethereum-1.8.7.sh | bash
curl -s https://raw.githubusercontent.com/oscm/shell/master/blockchain/ethereum/systemd/private.sh | bash

curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/node.js/binrary/node-v10.1.0.sh | bash
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/node.js/binrary/profile.d.sh | bash
curl -s https://raw.githubusercontent.com/oscm/shell/master/blockchain/ethereum/truffle/truffle.sh | bash

安装开发包

npm install express
npm install web3
npm install ejs

10.15.2. 主程序 main.js

var express = require('express');
var app = express();

app.use(express.static('public'));
app.set("view engine","ejs");
app.set('views', __dirname + '/views');  

var async = require('async');

fs = require('fs');
var net = require('net');
var Web3 = require('web3');
var web3 = new Web3('/home/ethereum/.ethereum/geth.ipc', net);
const abi = fs.readFileSync( __dirname + '/abi/NKC.abi', 'utf-8');
const coinbase = "0xaa96686a050e4916afbe9f6d62fa646dd8c51070"
const contractAddress = "0x5F75DA091aBb25e055B91172C04371Ff4Dd563a0";

console.log(web3.version)


app.get('/', function (req, res) {
  //  res.send('Hello World');
   res.render("index",{}); 
})

app.get('/account.html', function (req, res) {
  var accounts;
  web3.eth.getAccounts(function(err, acc) {
    accounts = acc
    res.render("account",{"accounts":accounts}); 
  });
})

app.get('/new', function (req, res) {
  web3.eth.personal.newAccount(req.query.password).then(function(){
    res.redirect('/account.html');
  });
})

app.get('/balance.html', function (req, res) {

  web3.eth.getAccounts(function(err, accounts) {
    res.render("balance",{"accounts":accounts}); 
  });
})
app.post('/showbalance.html', function (req, res) {
  // web3.eth.getBalance(req.query.account).then(function(balance){
  //   res.render("transfer",{"account":req.query.account, "balance": balance}); 
  // });
  
  res.render("showbalance",{"account": "sss", "balance": 1000}); 
})

app.get('/getbalance.html', function (req, res) {
  var contract = new web3.eth.Contract(JSON.parse(abi), contractAddress, { from: coinbase , gas: 100000});
  web3.eth.getBalance(req.query.account).then(function(balance){
    contract.methods.balanceOf(req.query.account).call().then(function(token){
      // console.log(contract.symbol.call());
      // contract.methods.symbol().call().then(console.log);
      contract.methods.symbol().call().then(function(name){
        res.render("showbalance",{"account":req.query.account, "balance": web3.utils.fromWei(balance, 'ether'), "token": token, "name": name}); 
      });
      
    });
    
  });
})

app.get('/transfer.html', function (req, res) {
  var contract = new web3.eth.Contract(JSON.parse(abi), contractAddress, { from: coinbase , gas: 100000});
  web3.eth.getAccounts(function(err, accounts) {
    contract.methods.symbol().call().then(function(symbol){
      res.render("transfer",{"accounts":accounts, "symbol": symbol}); 
    });
  });
})

app.get('/send', function (req, res) {
  // console.log(req.query)
  web3.eth.personal.unlockAccount(req.query.from, req.query.password).then(function(error){
    if(req.query.token == "ETH"){  
      web3.eth.sendTransaction({
        from: req.query.from,
        to: req.query.to,
        value: web3.utils.toWei(req.query.amount ,'ether')
      },
      function(error, result){
          if(!error) {
              console.log("#" + result + "#")
              res.render("done",{"hash":result}); 
          } else {
              console.error(error);
          }
      });
      
    }else{
      var contract = new web3.eth.Contract(JSON.parse(abi), contractAddress, { from: req.query.from , gas: 1000000});
      contract.methods.transfer(req.query.to, req.query.amount).send().then(function(hash){
        console.log(hash)
        res.render("done",{"hash":hash.transactionHash}); 
      });
    }
  });
})

var server = app.listen(8080, function () {
 
  var host = server.address().address
  var port = server.address().port
 
  console.log("应用实例,访问地址为 http://%s:%s", host, port)
 
})

10.15.3. ABI 文件 abi/NKC.abi

[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"burn","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_value","type":"uint256"}],"name":"burnFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initialSupply","type":"uint256"},{"name":"tokenName","type":"string"},{"name":"tokenSymbol","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","type":"event"}]			

10.15.4. 页面视图

10.15.4.1. views/account.ejs

<%- include header.ejs %>

<h1>Users</h1>
<ul id="accounts">
    <% accounts.forEach(function(account, index){ %>
    <li><%= index %>, <%= account %></li>
    <% }) %>
</ul>

<p>
新建账号
<form method="get" action="/new">
    密码:<input type="password" name="password" />
    <input type="submit" value="新建账号" />
</form>
</p>

10.15.4.2. views/balance.ejs

<%- include header.ejs %>

<h1>Account</h1>
<form method="get" action="/getbalance.html">

    <select name="account">
        <% accounts.forEach(function(account, index){ %>
        <option value ="<%= account %>"><%= account %></option>
        <% }) %>
    </select>
    <input type="submit" value="Submit" />
</form>

10.15.4.3. views/done.ejs

<%- include header.ejs %>

<p>转账完成</p>
<p>查看交易
<a href="https://etherscan.io/tx/<%= hash %>" target="etherscan">主网<%= hash %></a> <br />
</p>

10.15.4.4. views/header.ejs

<a href="/account.html">账号</a> | <a href="/balance.html">余额</a> | <a href="/transfer.html">转账</a>
<br /> 
<hr />

10.15.4.5. views/index.ejs

<%- include header.ejs %>

Welcome !!!

10.15.4.6. views/showbalance.ejs

<%- include header.ejs %>
<p>
<h1>Account: <%= account %>, Balance: <%= balance %></h1>
</p>

<p>
    Token: <%= token%> <%= name%> 
</p>

10.15.4.7. views/transfer.ejs

<%- include header.ejs %>

<h1>Account</h1>
<form method="get" action="/send">
    From:
    <select name="from">
        <% accounts.forEach(function(account, index){ %>
        <option value ="<%= account %>"><%= account %></option>
        <% }) %>
    </select>
    <br />
    To:
    <select name="to">
        <% accounts.forEach(function(account, index){ %>
        <option value ="<%= account %>"><%= account %></option>
        <% }) %>
    </select>

    <select name="token">
        <option value ="ETH">ETH</option>
        <option value ="<%= symbol %>"><%= symbol %></option>
    </select>

    <br />
    金额: <input type="text" name="amount" /> ETH
    <br />
    密码:<input type="password" name="password" />
    <br />
    <input type="submit" value="Submit" />
</form>

10.15.5. 启动 Node 服务

neo@MacBook-Pro ~/example % node main.js

浏览器访问 http://localhost:8080/ 可以进入钱包

您的打赏是我的写作动力:http://www.netkiller.cn/blockchain/donations.html

接受 ETH 打赏:0x3e827461Cc53ed7c75A29187CfF39629FCAE3661

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 以太坊 web3.py 签名转账

    中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890 <netkiller@msn.com>

    netkiller old
  • Solidity 智能合约开发语言·数据类型

    中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890 <netkiller@msn.com>

    netkiller old
  • CentOS 8 - dnf 安装 docker

    [root@localhost ~]# dnf config-manager --add-repo=https://download.docker.com/li...

    netkiller old
  • 小朋友学Java(7):继承

    在学习Java的继承之前,可以先复习一下C++的继承: http://www.jianshu.com/p/bc9b7ddb141c 一、定义 继承就是子类(派生...

    海天一树
  • Python3好用的原生api

    对列表进行反序是一个很常见的操作, 但python反向切片的玩法实在是非常简洁, 让人无法拒绝, 其实对某一数据结构进行"反向"是一个很有意...

    zhaoolee
  • Ajax请求($.ajax())中data属性传参数的形式

    实现Ajax提交数据进行请求,其中data属性设置传参的方法有好几种形式,如下: //第一种写法(把参数拼接在URL中,data属性设为空{ })

    公众号php_pachong
  • python——使用map快速匹配字典的

    场景很简单,如上面的代码,一个字典type_Dict,有五个键值对,随后我们新建了一个list并转换为Dataframe,列名为name。

    py3study
  • 用Python来分析一波周董新曲《说好不哭》为何如此火爆!

    其中可以看到两个重要参数:<fontcolor = “FF0000”>pagenum 和 <fontcolor = “FF0000”>pagesize

    YINUXY
  • java 通讯录Map接口实现

    葫芦
  • mybatis-generator使用备忘

    jeremyxu

扫码关注云+社区

领取腾讯云代金券