以太坊学习(5)利用Web3.js与节点交互【2】

利用Web3.js与节点交互【2】


这部分主要介绍利用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);
})
 

二、这里给出我们要操作的合约代码:

  • 合约内容比较简单
  • deposit()函数,对合约地址发起交易,可发送以太币。。相当于存钱到合约地址
  • event transfer()  事件
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;
  • abi是引入abi文件,并利用fs模块解析
  • myContractAddress是刚刚在部署合约后得到的合约地址
  • new web3.eth.Contract(abi)  //是web3 1.0版本的实例化方法
  • transferContract.options.address=myContractAddress; //实例化完成后,给合约对象绑定合约地址

 

七、接下来即可调用相关API了【要注意的是:web3.js 1.0版本基本上都是异步方式调用了

(1)调用合约中的deposit()方法,向合约发送以太币 

  • 由于该方法会改变合约的状态【合约地址下的以太币增加】,因此需要使用send调用,发起交易 
  • 这里给出官方原版文档地址:https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html
  • 这里给出中文版档案地址:http://cw.hubwiz.com/card/c/web3.js-1.0/1/4/9/

基本调用方法:

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()所发起交易的哈希

 八、接下来将代码优化一下

  • 将deposit().send()调用 写成函数的形式,并且将from后面的地址作为参数,即交易的发起方
  • 在获取到节点下的账户后,调用上述函数,并且将第0个账户作为参数,发起交易,来调用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);
}

最终执行效果:

可以看到,交易每被确认一次,回调函数会被触发一次。

以太坊学习(5)利用Web3.js与节点交互【2】_第1张图片

你可能感兴趣的:(区块链学习)