以太坊學習筆記:經過truffle-contract與智能合約交互

與以太坊的智能合約交互,除了使用web3.js,還可使用另一個Javascript庫,就是truffle-contract。truffle-contract對以太坊的智能合約作了更好的抽象,相比於web3.js,使用truffle-contract操做智能合約更加方便。truffle-contract具備如下特點:javascript

  • 同步的交易:能夠確保在交易生效以後再繼續執行其餘操做
  • 返回Promise:每一個封裝的合約函數會返回Promise,能夠對它進行.then操做,避免了回調地獄(callback hell)問題
  • 爲交易提供了默認參數:例如fromgas
  • 爲每一個同步的交易返回logs、交易receipt和交易hash

下面介紹如何在應用中使用truffle-contract與智能合約交互。java

系統和軟件

  • Ubuntu 16.04 64位
  • nodejs 6.10.0
  • npm 3.10.10

示例合約

仍是以上一篇文章中的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

安裝truffle-contract

首先新建一個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
相關文章
相關標籤/搜索