1. 程式人生 > >以太坊學習筆記(七)——Truffle整合Nodejs

以太坊學習筆記(七)——Truffle整合Nodejs

這篇文章的內容緊接著上章的內容,上一篇講了truffle合約部署的基本操作,文章最後講了通過console來呼叫部署成功的合約中的方法,之所以truffle console能夠直接呼叫合約中的方式,是因為truffle console預設集成了web3。如果想在NodeJS環境使用Truffle合約,就要手動整合這兩個模組。 整合NodeJS 1.首先需要安裝npm包管理器,這裡就不講安裝過程,如果已經安裝過最好把npm更新到最新版本,不然在後面的操作中可能會報錯,以下就是我在操作過程中遇到的一個錯誤。

 var BigNumber = (new Web3()).toBigNumber(0
).constructor; ^ TypeError: (intermediate value).toBigNumber is not a function

2.建立工程的npm包管理器,這裡我們還是以上一個專案為例,進入專案所在的目錄,使用npm init來初始化工程的npm包管理環境。

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to
guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (truffletest) truffletest //回車或輸入括號內的包名
version: (1.0.0) 1.0.0 //同上 license: (ISC) //同上 About to write to /Users/cyril/Desktop/truffleTest/package.json: { "name": "truffletest", "version": "1.0.0", "description": "truffle with nodejs", "main": "main.js", "directories": { "test": "test" }, "scripts": { "test": "truffletest" }, "repository": { "type": "git", "url": "main.js" }, "keywords": [ "truffletest" ], "author": "cyril", "license": "ISC", "dependencies": { "truffle-contract": "^3.0.6", "web3": "^1.0.0-beta2" } } Is this OK? (yes) yes //同前面的操作

這個時候專案會生成package.jsonpackage-lock.json同時會新增node-modules相關的庫依賴,生成的這些檔案沒必要去修改 專案結構如下: 安裝NodeJS中用到的Truffle合約抽象層執行環境 truffle-contractTruffle提供的,用於在NodeJS和瀏覽器中整合Truffle的合約抽象執行環境。

$ npm install truffle-contract

npm notice created a lockfile as package-lock.json. You should commit this file.
+ [email protected]3.0.6
added 24 packages from 18 contributors and audited 25 packages in 13.15s
found 0 vulnerabilities

安裝NodeJS中用到的Truffle執行時需要的web3環境

$ npm install web3

npm WARN deprecated web3@1.0.0-beta2: This version has faulty dependecy links.
+ web3@1.0.0-beta2
added 1 package from 1 contributor and audited 26 packages in 1.752s
found 0 vulnerabilities

測試合約 這裡就用前面的Test.solGreeter.sol,部署流程就不講了,在專案根目錄下建立main.js,內容大體結構如下

//引入依賴,並初始化一個例項Web3
var Web3 = require('web3');
var provider = new Web3.providers.HttpProvider("http://localhost:8545");

//省略了無關程式碼
//合約初始化
var Test = contract(/*合約JSON*/);//合約的元資料 

//設定連線
Test.setProvider(provider);

//設定預設地址
Test.defaults({
  from : "0x299127d72e28cb92d09f856aaedeb139d1e7e74a"
});

//函式呼叫
//TODO

我們拿Test舉個例子:

var Web3 = require('web3');
var contract = require("truffle-contract");

var provider = new Web3.providers.HttpProvider("http://localhost:8545");


//使用truffle-contract包的contract()方法
//請務必使用你自己編譯的.json檔案內容
var Test = contract({
    "contractName": "Test",
    "abi": [
        {
            "constant": true,
            "inputs": [],
            "name": "f",
            "outputs": [
                {
                    "name": "",
                    "type": "string"
                }
            ],
            "payable": false,
            "stateMutability": "pure",
            "type": "function"
        },
        {
            "constant": true,
            "inputs": [],
            "name": "g",
            "outputs": [
                {
                    "name": "",
                    "type": "string"
                }
            ],
            "payable": false,
            "stateMutability": "pure",
            "type": "function"
        }
    ],
    "networks": {
        "1536568575803": {
            "events": {},
            "links": {},
            "address": "0x57fec0a6d60ce7a133bbc6f7ac64f99a7b97425f",
            "transactionHash": "0x84463c7b8bce644a4df200ed1afdb3714514696cb32eef443854ca2dc2e56a70"
        },
        "1536655141628": {
            "events": {},
            "links": {},
            "address": "0x03873f72987988a143d514f43a6c548fe8d252b9",
            "transactionHash": "0xd3b7577fb5aae6a5950c0b2e04e1211f26ce487dda065d264bf6c61e2b453983"
        }
    },
    "schemaVersion": "2.0.1",
    "updatedAt": "2018-09-11T09:04:16.743Z"
});
Test.setProvider(provider);
//隨便從testrpc提供的地址中拷貝一個
Test.defaults({
    from: "0x807b0d9ffc27ecdc481bd1fc84fd3d4fb054c324" 
});

//呼叫合約方法
var instance;
Test.deployed().then(function (func) {
    instance = func;
    return instance.f.call();
}).then(function (value) {
    console.log(value);
    return instance.g.call();
}).then(function (value) {
    console.log(value);
});

注意: 1.最好原封不動的把json檔案的內容拷貝過來,為了節約篇幅,我就只貼上了部分,雖然不影響正常測試,但可能遇到下面的錯誤:

node_modules/truffle->contract/node_modules/web3/lib/web3/contract.js:56
 contract.abi.filter(function (json) {
               ^
TypeError: Cannot read property 'filter' of undefined

2.必須設定預設賬戶,truffle-contract框架預設沒有讀取coinbase的預設地址,所以需要主動設定,不然會報如下錯誤:

(node:26922) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: invalid address

執行main.js 執行main.js,然後報瞭如下錯:

$ node main.js 

module.js:487
    throw err;
    ^
Error: Cannot find module 'web3-requestManager'

這個問題就是我開篇說的一定要注意執行環境的版本,這個問題是因為package.json中依賴的web3版本是^1.0.0-beta2,這個問題可以通過安裝依賴解決

$ npm install ethereum/web3.js

+ web3@0.20.7
added 4 packages from 4 contributors, updated 1 package and audited 32 packages in 58.834s
found 0 vulnerabilities

再次檢視package.json中的web3版本已經變成了github:ethereum/web3.js,然後再次執行main.js

$ node main.js 

method f()
method g()

輸出結果跟上篇內容的一樣,也在預料之中。不論是合約中的f()方法還是g()方法,都不會改寫區塊鏈狀態,使用instance.f.call()呼叫;而對於一個會改寫區塊鏈狀態的函式f(),使用instance.f()呼叫,我們在以Greeter.sol為例來演示一下 下面的程式碼依然是放在main.js

var Web3 = require('web3');
var contract = require("truffle-contract");

var provider = new Web3.providers.HttpProvider("http://localhost:8545");

var Greeter = contract({
    "contractName": "Greeter",
    "abi": [
        {
            "inputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "constructor"
        },
        {
            "constant": true,
            "inputs": [],
            "name": "greet",
            "outputs": [
                {
                    "name": "",
                    "type": "string"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        },
        {
            "constant": false,
            "inputs": [
                {
                    "name": "_newgreeting",
                    "type": "string"
                }
            ],
            "name": "setGreeting",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
        },
        {
            "constant": false,
            "inputs": [],
            "name": "kill",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
        }
    ],
    "networks": {
        "1536568575803": {
            "events": {},
            "links": {},
            "address": "0x4cf91392df7740176d74f63faf9991ec838a6473",
            "transactionHash": "0x33b9776384f25342d51799970744b44dcfedf55a36093d55a72c5323dcbc8cf5"
        },
        "1536655141628": {
            "events": {},
            "links": {},
            "address": "0x2eb0113712145ffb42889c9421a6881e864ff2b4",
            "transactionHash": "0x748948df55b1597223d54cc6184d7a43ac47a04a559fafd9eb49e3c02c1abe98"
        }
    },
    "schemaVersion": "2.0.1",
    "updatedAt": "2018-09-11T09:04:16.744Z"
});

Greeter.setProvider(provider);
Greeter.defaults({
    from: "0xd10e318f0ef3e74c6e75e0827b840b2d00a49af5"
});

var ins;
Greeter.deployed().then(function (value) {
    ins = value;
    return ins.setGreeting("hello");
}).then(function (value) {
    console.log(value);
    return ins.greet.call();
}).then(function (value) {
    console.log(value);
});

執行

$ node main.js 
{ tx: '0x887d1043feb6f1e65bd90c71840b6b8fe8b2eea6ac19ad82dbd3c61c8eccae4c',
  receipt: 
   { transactionHash: '0x887d1043feb6f1e65bd90c71840b6b8fe8b2eea6ac19ad82dbd3c61c8eccae4c',
     transactionIndex: 0,
     blockHash: '0x323b5d22dbea9f4d7c1e70da5a0dc2b4b96f453afd6a3ae80c989470804c6562',
     blockNumber: 17,
     gasUsed: 33090,
     cumulativeGasUsed: 33090,
     contractAddress: null,
     logs: [],
     status: 1 },
  logs: [] }
//greet()方法輸出內容
hello

因為ins.setGreeting ()方法會改變區塊鏈狀態,所以需要消耗gas,同時在呼叫的時候返回對應的交易狀態結果。