简单项目实战
目的:以html+js构建网页,展示链上信息。
一、合约
- 合约内容:
- 用结构体user记录【用户】个人信息,映射为users
- 用结构体consignment记录【电能】交易订单,映射为orders
- 利用call获取订单的信息
- 合约如下:
pragma solidity ^0.4.13;
//pragma experimental ABIEncoderV2;
contract basecontract{
address administrator;
enum consignmentstatus{Waiting,Trading,Finished,Failed}
enum accountTradeStatus {Waiting,Buying,Selling}
event userRegisterLog(address addr);
event generatePowerConfirm(bool check,uint _value);
event getuserInfo(uint generateDepositPower,uint usablePower,uint debtPower);
event consumePowerEvent(address indexed addre,bool success,uint consumelocalpower,uint consumepurchasepower);
struct user{
string name;
string useraddress;
uint generateDepositPower;
uint totalgeneratePower;
uint usablePower;
uint totalConsumePower;
uint debtPower;
bool generateRight;
bool useRight;
accountTradeStatus status;
//bool generatingPower;
//bool usingPower;
uint orderID;
}
struct consignment{
address consignmentSender;
address consumer;
consignmentstatus status;
uint energyRemainToTrade;
uint totalEnergytoSell;
uint price;
uint dealTime;
}
mapping(address=>user) public users;
mapping(uint=>consignment) public orders;
uint public latestOrderID =1;
function basecontract() { administrator = msg.sender; }
function getUserInfo(address _addr) view public returns(uint,uint,uint)
{
require(msg.sender==administrator || msg.sender==_addr);
return(users[_addr].generateDepositPower,
users[_addr].usablePower,users[_addr].debtPower);
}
function getConsignment(uint _id) view public returns(address,uint,uint,uint,uint,consignmentstatus)
{
require(_id<=(2**255-1)&&_id<latestOrderID);
return(
orders[_id].consignmentSender,
orders[_id].energyRemainToTrade,
orders[_id].totalEnergytoSell,
orders[_id].price,
orders[_id].dealTime,
orders[_id].status
);
}
}
//合约非完整版,只展示了call部分方法
- 假定users以及orders两个映射已经有内容,我们直接call即可得到对应信息。
- 内容如下:
二、以node.js架构本地服务器【node.js安装不再赘述】
(1) 新建项目文件夹
mkdir htmlTest
cd htmlTest
(2)安装express
npm install express
(3) 新建server.js
var express = require("express");
var app = express();
app.use(express.static("public")).listen(8080);
(4)新建文件夹public,用来存放index.html以及各种js文件等
三、编写index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<h1>交易系统Test</h1>
<hr>
</hr>
<h2>用户信息
</h2>
<table border="0">
<tr>
<td><button type="button" id="getAccountBtn">getAccout</button></td>
<td><select id="eth_account"></select></td>
<td>
<div id="eth_balance">balance</div>
</td>
</tr>
</table>
<ul id="userinfo">
<li id="userinfo_generateDepositPower">本地储存电量:</li>
<li id="userinfo_usablePower">已购可用电量:</li>
<li id="userinfo_debtPower">欠费电量:</li>
</ul>
<hr>
</hr>
<h2>寄售列表<button type="button" id="refresh_consignment">刷新</button>
</h2>
<table id="consignmentList" border="1">
<tr id="table_head">
<td>寄售账户</td>
<td>待传输电量</td>
<td>总交易电量</td>
<td>寄售价格</td>
<td>交易时间</td>
<td>交易状态</td>
</tr>
</table>
</body>
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js@1.0.0-beta.36/dist/web3.min.js" integrity="sha256-nWBTbvxhJgjslRyuAKJHK+XcZPlCnmIAAMixz6EefVk=" crossorigin="anonymous"></script>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="eth.js"></script>
</html>
注意事项:
- 基本划分两部分,一是获取用户信息,二是获取订单信息
- 直接引入了web3.js,就不用在js里面使用require("web3")了,因为这里只是javascript文件,与直接在node.js环境下运行有所区别。<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js@1.0.0-beta.36/dist/web3.min.js" integrity="sha256-nWBTbvxhJgjslRyuAKJHK+XcZPlCnmIAAMixz6EefVk=" crossorigin="anonymous"></script>
- 引入jquery库
- eth.js为自己写的js脚本
界面如下:
四、eth.js文件编写
关于web3.js的使用,可参考:利用Web3.js与节点交互【1】 、利用Web3.js与节点交互【2】
以及web3.js 1.0版本官方文档:
中文版:http://cw.hubwiz.com/card/c/web3.js-1.0/1/4/8/
官方版:https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract
(1)web3定义以及连接本地节点
let web3;
//文档构筑完成就执行如下:
$(document).ready(function(){
if(typeof web3 !=='undefined'){ //检查是否已有web3实例
web3=new Web3(web3.currentProvider);
}else{
//否则就连接到给出节点
web3=new Web3();
web3.setProvider(new Web3.providers.WebsocketProvider("ws://localhost:8546"));//注意这里注意端口不用一致,直接默认8546即可(若刚刚启动节点的rpc端口是8545的情况下)
}
//检查连接情况
web3.eth.getBlock(0, function(error, result){
if(!error)
console.log("connect should be success");
else
console.log("something wrong,the connection might be failed");
})
(2)实例化合约
var myContract;
//实例化合约
var abi;
$.ajax({
url: 'contract.json',
async: false,
success: function (data) {
abi = data;
}
});
var myContractAddress="0xbb877827e6ef3870c96c7a7726e8e2bed140fb9e";
myContract=new web3.eth.Contract(abi);
myContract.options.address=myContractAddress;
});
- 关于abi,contract.json文件为自己在public文件夹下创建,内容为合约的abi,即json,可在remix-ide中获取
- 合约地址为该合约部署到私有链上的地址
- 如果这一段可正常执行,那么接下来就可以用myContract直接操作合约
(3) getAccount按钮:获取节点下的账户,然后添加到下拉列表框中
//get node_accounts
let account;
$("#getAccountBtn").click(function(){
console.log('attempt to get accounts');
web3.eth.getAccounts(function(error, result){
if(!error){
//清空列表框
$("#eth_account").empty();
//重新插入
for(var i=0;i<result.length;i++){
$("#eth_account").append("<option value='"+result[i]+"'>"+result[i]+"</option>");
//console.log(result[i]);
}
account=result;
}
else{
console.log("failed to get Accoutns");
}
})
});
(4)获取账户余额:下拉列表框选中项改变时触发
- 获取的余额为下拉列表框当前选中的地址项
//get balance
$("#eth_account").change(function(event) {
var _account=$("#eth_account").val();
web3.eth.getBalance(_account).then(function(balance){
console.log('account: ',_account,' balance:',web3.utils.fromWei(balance),'ether');
$("#eth_balance").text(web3.utils.fromWei(balance)+' ether')
});
getUserInfo();
});
(5)获取用户信息:这里就要开始使用myContract了,因为调用的是合约的Call方法
- 获取到的信息填充到列表下
//获取账户信息
function getUserInfo(){
var _account=$("#eth_account").val();
myContract.methods.getUserInfo(_account).call({from:_account},
function(error,result){
$("#userinfo_generateDepositPower").text('本地储存电量:'+result[0]);
$("#userinfo_usablePower").text('已购可用电量:'+result[1]);
$("#userinfo_debtPower").text('欠费电量:'+result[2]);
console.log(result);
}
);
}
(6)获取寄售订单:点击刷新按钮触发,然后将获取到的订单信息填入表格中
//获取寄售订单
$("#refresh_consignment").click(function(event) {
var _account=$("#eth_account").val();
for(var i=1;i<=1;i++){
myContract.methods.getConsignment(i).call({from:_account},
function(error,result){
var tr=$("<tr>\
<td>"+result[0]+"</td>"+
"<td>"+result[1]+"</td>"+
"<td>"+result[2]+"</td>"+
"<td>"+result[3]+"</td>"+
"<td>"+result[4]+"</td>"+
"<td>"+result[5]+"</td></tr>");
$("#consignmentList").append(tr);
console.log(result);
}
);
}
});
(7)eth.js全部代码如下:
let web3;
var myContract;
$(document).ready(function(){
if(typeof web3 !=='undefined'){ //检查是否已有web3实例
web3=new Web3(web3.currentProvider);
}else{
//否则就连接到给出节点
web3=new Web3();
web3.setProvider(new Web3.providers.WebsocketProvider("ws://localhost:8546"));//注意这里注意端口不用一致,直接默认8546即可(若刚刚启动节点的rpc端口是8545的情况下)
}
//var connected = web3.isConnected();
web3.eth.getBlock(0, function(error, result){
if(!error)
console.log("connect should be success");
else
console.log("something wrong,the connection might be failed");
})
//实例化合约
//var abi=JSON.parse(fs.readFileSync("basecontract_sol_basecontract.abi").toString())
var abi;
$.ajax({
url: 'contract.json',
async: false,
success: function (data) {
abi = data;
}
});
var myContractAddress="0xbb877827e6ef3870c96c7a7726e8e2bed140fb9e";
myContract=new web3.eth.Contract(abi);
myContract.options.address=myContractAddress;
});
//get node_accounts
let account;
$("#getAccountBtn").click(function(){
console.log('attempt to get accounts');
web3.eth.getAccounts(function(error, result){
if(!error){
//清空列表框
$("#eth_account").empty();
//重新插入
for(var i=0;i<result.length;i++){
$("#eth_account").append("<option value='"+result[i]+"'>"+result[i]+"</option>");
//console.log(result[i]);
}
account=result;
}
else{
console.log("failed to get Accoutns");
}
})
});
//get balance
$("#eth_account").change(function(event) {
var _account=$("#eth_account").val();
web3.eth.getBalance(_account).then(function(balance){
console.log('account: ',_account,' balance:',web3.utils.fromWei(balance),'ether');
$("#eth_balance").text(web3.utils.fromWei(balance)+' ether')
});
getUserInfo();
});
//获取账户信息
function getUserInfo(){
var _account=$("#eth_account").val();
myContract.methods.getUserInfo(_account).call({from:_account},
function(error,result){
$("#userinfo_generateDepositPower").text('本地储存电量:'+result[0]);
$("#userinfo_usablePower").text('已购可用电量:'+result[1]);
$("#userinfo_debtPower").text('欠费电量:'+result[2]);
console.log(result);
}
);
}
//获取寄售订单
$("#refresh_consignment").click(function(event) {
var _account=$("#eth_account").val();
for(var i=1;i<=1;i++){
myContract.methods.getConsignment(i).call({from:_account},
function(error,result){
var tr=$("<tr>\
<td>"+result[0]+"</td>"+
"<td>"+result[1]+"</td>"+
"<td>"+result[2]+"</td>"+
"<td>"+result[3]+"</td>"+
"<td>"+result[4]+"</td>"+
"<td>"+result[5]+"</td></tr>");
$("#consignmentList").append(tr);
console.log(result);
}
);
}
});
五、启动server.js
nodejs server.js
六、 浏览器输入:http://localhost:8080/
(1)点击getAccount
(2)点击寄售列表后面的刷新按钮: