这部分主要介绍利用web3.js对已经部署的合约进行一些操作等
写在最前:web3.js 1.0版本跟 0.2.x版本的使用区别比较大,这里暂时先给出1.0版本下的调用
web3.js 1.0 官方文档:https://web3js.readthedocs.io/en/1.0/web3.html#value
一、继续沿用(利用Web3.js与节点交互【1】)的代码
//引入模块
let Web3=require('web3');
let fs=require('fs');
let web3;
//连接到节点
if(typeof web3 !=='undefined'){ //检查是否已有web3实例
web3=new Web3(web3.currentProvider);
}else{
//否则就连接到给出节点
web3=new Web3();
web3.setProvider(new Web3.providers.WebsocketProvider("ws://localhost:8546"));
}
//通过查询创世区块来判断是否连接成功
web3.eth.getBlock(0, function(error, result){
if(!error)
console.log("connection should be succeed");
else
console.log("something wrong,the connection might be failed");
})
//get node_accounts 获取节点下的账户
let account0;
web3.eth.getAccounts(function(error, result){
if(!error){
account0=result[0];
//console.log(account0);
console.log("accounts:"+result);
}
else{
console.log("failed to get Accoutns");
}
})
//get balance 获取指定账户下的余额
web3.eth.getBalance('0x2EC12C8e3a8a0df6EdD448688e6EE0E0132ED801').then(function(balance){
console.log('balance:',balance);
})
二、这里给出我们要操作的合约代码:
pragma solidity ^0.4.0;
contract Transfer{
event transfer(address indexed _from, address indexed _to, uint indexed value);
function deposit() payable {
address current = this;
uint value = msg.value;
transfer(msg.sender, current, value);
}
function getBanlance() constant returns(uint) {
return this.balance;
}
/* fallback function */
function(){}
}
三、部署合约【这里用geth+在线编辑器remix部署】
(1)合约部署请参见:以太坊学习(5)编写并部署一个智能合约
(2)注意选择的web3 provider 的端口是8545,与启动节点时的rpcport一致,与node.js代码中的websocket端口不一致
四、部署完成之后,我这里获得的合约地址是:0x15a27fb5ea1c3842fac984df488df9325ef60829,
五、编译获取abi
(1)方式一:直接在remix网页版中可以获取json格式的文本
(2)方式二【也是本案例选择的方式】:利用solc编译
- 将合约代码保存到本地,文件名:mycontract.sol
- 安装solc -----------> npm install -g solc
- 获取abi ------------> solcjs --abi mycontract.sol
- 查看本地文件,会发现多了一个abi文件 mycontract_sol_Transfer.abi
(3)方式三:直接在nodejs里面引用solc进行编译
六、基本准备已经完成,可以在node.js文件里面引入合约了
(1)在文件头引入fs模块,用来解析abi文件到json格式
let fs=require('fs');
(2) 新建合约对象并实例化到地址,只需4行代码
var abi=JSON.parse(fs.readFileSync("mycontract_sol_Transfer.abi").toString())
var myContractAddress="0x15a27fb5ea1c3842fac984df488df9325ef60829";
var transferContract =new web3.eth.Contract(abi);
transferContract.options.address=myContractAddress;
七、接下来即可调用相关API了【要注意的是:web3.js 1.0版本基本上都是异步方式调用了】
(1)调用合约中的deposit()方法,向合约发送以太币
基本调用方法:
send----------向合约发送交易来执行指定方法,将改变合约的状态。
调用:
myContract.methods.myMethod([param1[, param2[, ...]]]).send(options[, callback])
参数:
- options - Object: 选项,包含如下字段:
- from - String: 交易发送方地址
- gasPrice - String : 可选,用于本次交易的gas价格,单位:wei
- gas - Number : 可选,本次交易的gas用量上限,即gas limit
- value - Number|String|BN|BigNumber: 可选,交易转账金额,单位:wei
- callback - Function: 可选的回调参数,其参数为交易哈希值和错误对象
那么,针对本案例,下面给出调用deposit()方法的代码:
transferContract.methods.deposit().send({
from:"0x2EC12C8e3a8a0df6EdD448688e6EE0E0132ED801", //发起交易的账户
value:100, //交易的金额
gas:10000000
});
或者异步回调
transferContract.methods.deposit().send({
from:"0x2EC12C8e3a8a0df6EdD448688e6EE0E0132ED801", //发起交易的账户
value:100, //交易的金额
gas:10000000
},function(error, transactionHash){ //回调返回交易哈希
if(!error)
console.log(transactionHash);
else
console.log(error);
});
结合前面部分的代码,本文到这里的全部代码如下:
//引入模块
let Web3=require('web3');
let fs=require('fs');
let web3;
//连接到节点
if(typeof web3 !=='undefined'){ //检查是否已有web3实例
web3=new Web3(web3.currentProvider);
}else{
//否则就连接到给出节点
web3=new Web3();
web3.setProvider(new Web3.providers.WebsocketProvider("ws://localhost:8546"));
}
//通过查询创世区块来判断是否连接成功
web3.eth.getBlock(0, function(error, result){
if(!error)
console.log("connection should be succeed");
else
console.log("something wrong,the connection might be failed");
})
//get node_accounts 获取节点下的账户
let account0;
web3.eth.getAccounts(function(error, result){
if(!error){
account0=result[0];
//console.log(account0);
console.log("accounts:"+result);
}
else{
console.log("failed to get Accoutns");
}
})
//get balance 获取指定账户下的余额
web3.eth.getBalance('0x2EC12C8e3a8a0df6EdD448688e6EE0E0132ED801').then(function(balance){
console.log('balance:',balance);
})
//创建合约实例
var abi=JSON.parse(fs.readFileSync("mycontract_sol_Transfer.abi").toString())
var myContractAddress="0x15a27fb5ea1c3842fac984df488df9325ef60829";
var transferContract =new web3.eth.Contract(abi);
transferContract.options.address=myContractAddress;
//调用deposit()方法,利用send方法调用
transferContract.methods.deposit().send({
from:"0x2EC12C8e3a8a0df6EdD448688e6EE0E0132ED801", //发起交易的账户
value:100, //交易的金额
gas:10000000
},function(error, transactionHash){ //回调返回交易哈希
if(!error)
console.log(transactionHash);
else
console.log(error);
});
解锁账户后,运行如下:
可以看见最后返回的是调用deposit()所发起交易的哈希
八、接下来将代码优化一下
写成函数形式:
function send_deposit(_transactionFrom){
console.log("sending the Transaction:");
transferContract.methods.deposit().send({
from:_transationFrom, //发起交易的账户
value:100, //交易的金额
gas:10000000
},function(error, transactionHash){ //回调返回交易哈希
if(!error)
console.log(transactionHash);
else
console.log(error);
});
}
//get balance 获取指定账户下的余额
function GetBalance(_account){
web3.eth.getBalance(_account).then(function(balance){
console.log('balance:',balance);
});
}
另外,为了不用每次都到geth去解锁账户,再加上一个解锁账户的函数【web3.js 1.0好像又取消了解锁账户功能。。有待研究】
以上两个函数都应该在获取账户后才能调用,因此,将其调用放在getAccount函数的回调中进行调用
优化后的完整代码如下:
【到这里的话,代码中就没有了账户地址显式出现了,只有一个合约地址】
//引入模块
let Web3=require('web3');
let fs=require('fs');
let web3;
//连接到节点
if(typeof web3 !=='undefined'){ //检查是否已有web3实例
web3=new Web3(web3.currentProvider);
}else{
//否则就连接到给出节点
web3=new Web3();
web3.setProvider(new Web3.providers.WebsocketProvider("ws://localhost:8546"));
}
//通过查询创世区块来判断是否连接成功
web3.eth.getBlock(0, function(error, result){
if(!error)
console.log("connection should be succeed");
else
console.log("something wrong,the connection might be failed");
})
//get node_accounts 获取节点下的账户
let account0;
web3.eth.getAccounts(function(error, result){
if(!error){
account0=result[0];
GetBalance(account0);//获取账户余额
send_deposit(account0); //将获取到的第0个账户作为参数
}
else{
console.log("failed to get Accoutns");
}
})
//get balance 获取指定账户下的余额
function GetBalance(_account){
web3.eth.getBalance(_account).then(function(balance){
console.log('account0:',_account,' balance:',balance);
});
}
//创建合约实例
var abi=JSON.parse(fs.readFileSync("mycontract_sol_Transfer.abi").toString())
var myContractAddress="0x15a27fb5ea1c3842fac984df488df9325ef60829";
var transferContract =new web3.eth.Contract(abi);
transferContract.options.address=myContractAddress;
//调用deposit()方法,利用send方法调用
function send_deposit(_transactionFrom){
transferContract.methods.deposit().send({
from:_transactionFrom, //发起交易的账户
value:100, //交易的金额
gas:10000000
},function(error, transactionHash){ //回调返回交易哈希
if(!error)
console.log('the transationHash is',transactionHash);
else
console.log(error);
});
}
九、最后的问题
我们需要确认交易是否已经被节点确认
可以通过检查合约的余额来判断
(1)那么这里send_deposit()方法中的deposit().send()的回调需要使用另一种回调方式:event emitter 【类似于事件的回调】
修改后的的send_deposit()方法:
//调用deposit()方法,利用send方法调用
function send_deposit(_transactionFrom){
transferContract.methods.deposit().send({
from:_transactionFrom, //发起交易的账户
value:100, //交易的金额
gas:10000000
})
.on('transactionHash', function(hash){
console.log(hash);
})
.on('confirmation', function(confirmationNumber, receipt){
GetBalance(myContractAddress); //确认后再次查询合约余额
console.log('the confirmationNumber is',confirmationNumber);//输出确认区块数
})
.on('error', console.error);
}
(2)给出最后到这里的全部代码:
//引入模块
let Web3=require('web3');
let fs=require('fs');
let web3;
//连接到节点
if(typeof web3 !=='undefined'){ //检查是否已有web3实例
web3=new Web3(web3.currentProvider);
}else{
//否则就连接到给出节点
web3=new Web3();
web3.setProvider(new Web3.providers.WebsocketProvider("ws://localhost:8546"));
}
//通过查询创世区块来判断是否连接成功
web3.eth.getBlock(0, function(error, result){
if(!error)
console.log("connection should be succeed");
else
console.log("something wrong,the connection might be failed");
})
//get node_accounts 获取节点下的账户
let account0;
web3.eth.getAccounts(function(error, result){
if(!error){
account0=result[0];
GetBalance(account0);//获取账户余额
GetBalance(myContractAddress);//获取合约账户下的余额
send_deposit(account0); //将获取到的第0个账户作为参数
}
else{
console.log("failed to get Accoutns");
}
})
//get balance 获取指定账户下的余额
function GetBalance(_account){
web3.eth.getBalance(_account).then(function(balance){
console.log('account0:',_account,' balance:',balance);
});
}
//创建合约实例
var abi=JSON.parse(fs.readFileSync("mycontract_sol_Transfer.abi").toString())
var myContractAddress="0x15a27fb5ea1c3842fac984df488df9325ef60829";
var transferContract =new web3.eth.Contract(abi);
transferContract.options.address=myContractAddress;
//调用deposit()方法,利用send方法调用
function send_deposit(_transactionFrom){
transferContract.methods.deposit().send({
from:_transactionFrom, //发起交易的账户
value:100, //交易的金额
gas:10000000
})
.on('transactionHash', function(hash){
console.log(hash);
})
.on('confirmation', function(confirmationNumber, receipt){
GetBalance(myContractAddress);
console.log('the confirmationNumber is',confirmationNumber);
})
.on('error', console.error);
}
最终执行效果:
可以看到,交易每被确认一次,回调函数会被触发一次。