与以太坊的智能合约交互,除了使用web3.js,还可使用另一个Javascript库,就是truffle-contract。truffle-contract对以太坊的智能合约作了更好的抽象,相比于web3.js,使用truffle-contract操做智能合约更加方便。truffle-contract具备如下特点:javascript
.then
操做,避免了回调地狱(callback hell)问题from
或gas
下面介绍如何在应用中使用truffle-contract与智能合约交互。java
仍是以上一篇文章中的MetaCoin合约为例,确保启动了一个以太坊节点,而且已将下面的MetaCoin合约部署到节点中。node
// 本文中用到的MetaCoin合约 pragma solidity ^0.4.2; contract MetaCoin { mapping (address => uint) balances; event Transfer(address indexed _from, address indexed _to, uint256 _value); function MetaCoin() { balances[tx.origin] = 10000; } function sendCoin(address receiver, uint amount) returns(bool sufficient) { if (balances[msg.sender] < amount) return false; balances[msg.sender] -= amount; balances[receiver] += amount; Transfer(msg.sender, receiver, amount); return true; } function getBalance(address addr) returns(uint) { return balances[addr]; } }
接下来就能够按照下面的步骤在项目中经过truffle-contract来使用这个合约。git
首先新建一个Nodejs项目并初始化:github
$ mkdir truffle-contract-test && cd truffle-contract-test $ npm init
接下来安装truffle-contract:web
$ npm install web3 --save $ npm install truffle-contract --save
注:安装truffle-contract并不要求先安装web3,这里安装web3是由于后面会用到。npm
与web3.js相似,要使用truffle-contract,须要先初始化合约对象,而后链接到一个以太坊节点。在truffle-contract-test目录下新建index.js文件,在其中输入如下代码:数组
var Web3 = require("web3"); var contract = require("truffle-contract"); // 合约ABI var contract_abi = [{ "constant": false, "inputs": [{ "name": "receiver", "type": "address" }, { "name": "amount", "type": "uint256" }], "name": "sendCoin", "outputs": [{ "name": "sufficient", "type": "bool" }], "payable": false, "type": "function" }, { "constant": false, "inputs": [{ "name": "addr", "type": "address" }], "name": "getBalance", "outputs": [{ "name": "", "type": "uint256" }], "payable": false, "type": "function" }, { "inputs": [], "payable": false, "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" }]; // 经过ABI初始化合约对象 var MetaCoin = contract({ abi: contract_abi // 若是要部署合约,还要指定合约代码: // unlinked_binary: ... }); // 链接到以太坊节点 var provider = new Web3.providers.HttpProvider("http://localhost:8545"); MetaCoin.setProvider(provider);
接下来,可使用MetaCoin的如下三个函数:app
at(contract_address)
:经过指定的合约地址获取MetaCoin合约实例deployed()
:经过默认的合约地址获取MetaCoin合约实例new()
:部署新的合约,而且获取新部署的合约实例能够像下面这样调用getBalance
函数:ide
// 改为你本身的帐户地址 var account_one = "0x68b73956d704007514e9257813bdc58cdf3c969a"; // 合约地址,改为你本身的合约地址 var contract_address = "0xb2cdd356e58280906ce53e1665697b50f88aac56"; MetaCoin.at(contract_address).then(function(instance){ return instance.getBalance.call(account_one); }).then(function(balance){ console.log(balance.toNumber()); }).catch(function(err){ console.log(err); });
首先经过MetaCoin.at()
获取合约实例,在.then
链式调用中,回调函数会在参数中获取到合约实例instance
,接下来就能够在回调函数中使用合约实例来调用getBalance
函数,再继续经过.then
链式调用,经过回调函数得到getBalance
的返回值balance
。
再来看看调用sendCoin
函数的状况:
// 改为你本身的帐户地址 var account_one = "0x68b73956d704007514e9257813bdc58cdf3c969a"; var account_two = "0x9c3c1a2f5ef913fac44f0348a78f68d835f3f26e"; // 合约地址,改为你本身的合约地址 var contract_address = "0xb2cdd356e58280906ce53e1665697b50f88aac56"; MetaCoin.at(contract_address).then(function(instance){ // 调用sendCoin会向区块链发送一笔交易 return instance.sendCoin(account_two, 100, {from:account_one}); }).then(function(result){ // 这个回调函数在交易生效以后才会被执行 // result对象包含如下三个字段: // result.tx => 交易hash,是一个string // result.receipt => 交易执行结果,是一个对象 // result.logs => 交易产生的事件集合,是一个对象数组 console.log(result); }).catch(function(err){ console.log(err); });
这里,调用sendCoin
会向区块链发送一笔交易,在交易生效以后,才会执行回调函数,回调函数的参数中包含了交易hash、交易执行结果以及交易产生的事件。
能够经过result.logs
获取交易触发的事件:
// 改为你本身的帐户地址 var account_one = "0x68b73956d704007514e9257813bdc58cdf3c969a"; var account_two = "0x9c3c1a2f5ef913fac44f0348a78f68d835f3f26e"; // 合约地址,改为你本身的合约地址 var contract_address = "0xb2cdd356e58280906ce53e1665697b50f88aac56"; MetaCoin.at(contract_address).then(function(instance){ // 调用sendCoin会向区块链发送一笔交易 return instance.sendCoin(account_two, 100, {from:account_one}); }).then(function(result){ // 这个回调函数在交易生效以后才会被执行 // result.logs是一个数组,数组的每一个元素是一个事件对象 // 经过查询result.logs能够得到感兴趣的事件 for (var i = 0; i < result.logs.length; i++) { var log = result.logs[i]; if (log.event == "Transfer") { console.log("from:", log.args._from); console.log("to:", log.args._to); console.log("amount:", log.args._value.toNumber()); break; } } }).catch(function(err){ console.log(err); }); // 输出: from: 0x68b73956d704007514e9257813bdc58cdf3c969a to: 0x9c3c1a2f5ef913fac44f0348a78f68d835f3f26e amount: 100
sendCoin
执行完后会触发一个Transfer
事件,在回调函数中,经过查询result.logs
能够获取到这个事件,进而能够获得事件的参数值。
下面是一个完整的例子:
var Web3 = require("web3"); var contract = require("truffle-contract"); // 合约ABI var contract_abi = [{ "constant": false, "inputs": [{ "name": "receiver", "type": "address" }, { "name": "amount", "type": "uint256" }], "name": "sendCoin", "outputs": [{ "name": "sufficient", "type": "bool" }], "payable": false, "type": "function" }, { "constant": false, "inputs": [{ "name": "addr", "type": "address" }], "name": "getBalance", "outputs": [{ "name": "", "type": "uint256" }], "payable": false, "type": "function" }, { "inputs": [], "payable": false, "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" }]; // 经过ABI初始化合约对象 var MetaCoin = contract({ abi: contract_abi // 若是要部署合约,还要指定合约代码: // unlinked_binary: ... }); // 链接到以太坊节点 var provider = new Web3.providers.HttpProvider("http://localhost:8545"); MetaCoin.setProvider(provider); // 改为你本身的帐户地址 var account_one = "0x68b73956d704007514e9257813bdc58cdf3c969a"; var account_two = "0x9c3c1a2f5ef913fac44f0348a78f68d835f3f26e"; // 合约地址,改为你本身的合约地址 var contract_address = "0xb2cdd356e58280906ce53e1665697b50f88aac56"; var coin; MetaCoin.at(contract_address).then(function(instance){ coin = instance; // 首先查看帐户二的余额 return coin.getBalance.call(account_two); }).then(function(balance_of_account_two){ console.log("Balance of account two is", balance_of_account_two.toNumber()); // 从帐户一贯帐户二转100个币 return coin.sendCoin(account_two, 100, {from:account_one}); }).then(function(result){ console.log("Sent 100 coin from account one to account two."); // 再次查看帐户二的余额 return coin.getBalance.call(account_two); }).then(function(balance_of_account_two){ console.log("Balance of account two is", balance_of_account_two.toNumber()); }).catch(function(err){ console.log("Error:", err.message); });
// 输出 Balance of account two is 3400 Sent 100 coin from account one to account two. Balance of account two is 3500