以太坊Solidity編程:合約調用與Web3.js
合約部署方法
合約的編譯
- 使用瀏覽器編譯器Remix
- 使用truffle編譯,目前是最常用的編譯方式
- Solc或者Web3.js編譯合約,使用相對較少
基於Remix的編譯部署
- Remix直接編譯即可
- 部署使用remix的web3 provider形式
- 部署也可用remix+metamask,但metamask安裝需要科學上網
- Remix訪問的時候,建議使用http,而不是https
Truffle編譯
- 合約應該位於./contracts目錄
- 編譯合約命令:truffle compile
- Truffle僅僅默認自上次編譯後被修改過的文件,來減少不必要的編譯。如果想要編譯全部文件,可以使用–compile-all選項。
Truffle編譯約定
- Truffle需要定義的合約名稱和文件名準確匹配,如文件名爲MyContract.sol,那麼合約文件須爲如下兩者之一:
- contract MyContract {…}
- library MyContract {…}
- Truffle文件名匹配是區分大小寫的,也就是說大小寫也要一致。
- 可以通過使用import來聲明依賴。Truffle將會按正確順序依次編譯合約,並在需要的時候自動關聯庫。
- 編譯的輸出位於./build/contracts目錄。如果目錄不存在會自動創建。
Truffle部署
Truffle通過truffle.js指定的以太坊網絡來部署,部署命令爲truffle deploy,如果truffle.js有多個網絡,可以使用networkc參數來指定。
Truffle配置文件
配置文件是truffle.js。位於項目的根目錄下。這個文件是Javascript文件,支持執行代碼來創建配置:
- BUILD:這個是前端的構建配置。默認調用默認構建器。
- NETWORKS:指定在部署時使用哪個網絡。
- RPC:關於如何連接到以太坊客戶端的一些細節。host和port是必須的。還包括gas(部署時的Gas限制),gasPrice(部署時的Gas價格),from(移植時使用的源地址,默認是你的以太坊客戶端第一個可用賬戶).
Truffle配置文件示例:
module.export = {
networks:{
development:{
host:"localhost",
port:8545,
network_id:"*" //匹配任何network id
}
}
};
Truffle deployer部署參數示例:
var Hello = artifacts.require("./Hello.sol");
var Multi = artifacts.require("./Multi.sol");
module.exports = function(deployer){
//部署單個合約,不帶任何構造參數
deployer.deploy(Hello);
//部署單個合約帶有構造參數
deployer.deploy(Multi,11);
//部署多個合約,一些有參數,一些沒有參數
/*
deployer.deploy([
[A,arg1,arg2,...],
B,
[C,arg1]
]);
*/
}
合約調用與web3.api
智能合約調用
以太坊支持通過各種方式與節點進行交互:
- JSON-RPC
- JavaScript Console
- web3
JSON RPC
- JSON RPC可以理解爲一個rest服務
- 大部分客戶端均通過JSON RPC調功能、傳數據
- JSON RPC只是一個傳輸通道,以太坊還有IPC的接口。
RPC調用客戶端命令
假設我們要調用客戶端命令eth.getBalance(),查詢地址爲0x407的賬號的餘額,命令如下:
curl --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x407","latest"],"id":1}' localhost:8123
其中,jsonrpc字段指定JSON-RPC版本號,method字段指定需要調用的api方法名,params字段爲傳送的參數,id爲消息標識字段;
RPC調用智能合約
假設目前有部署的智能合約,地址爲0x6ff93,我們要調用的合約方法簽名multiply(uint256),傳入的參數值爲6,那麼調用命令的格式如下:
curl --data
{
"jsonrpc":"2.0",
"method":"eth_sendtransaction",
"param":[{
"from":"0xeb85a5",
"to":"0x6ff93",
"data":"0xcddddd"
}],
"id":8
}
localhost:8123
其中,from爲扣除GAS賬戶地址,to爲智能合約部署的地址,data爲調用方法的簽名和傳入參數,編碼方式爲“0x”+sha3(“multiply(uint256)”).substring(0,8)+to_32bit_Hex_str(6)
RPC合約調用
直接使用RPC對智能合約的調用需要進行復雜的編碼。具體規則可以參考Ethereum Contract ABI文檔。實際編程中,基本都使用web3等方式,對RPC進行了友好封裝。
Web3概述
- 與合約交互,最常用的方式就是使用web3.js library提供的web3
- 底層實現上,它通過RPC調用與本地節點通信
- web3.js可以與任何暴露了RPC接口的以太坊節點連接。
- Web3已隨truffle安裝。
Web3調用合約
- web3.js封裝了合約調用的方法。
- 使用可以直接使用web3.eth.contract的裏的sendTransaction來修改區塊鏈數據
- 調用合約,可能需要from等參數,否則可能出現調用異常。
Web3 API體系
- Web3-eth:以太坊區塊鏈基本操作和智能合約相關操作。
- Web3-ssh:實現whisper相關操作,包括p2p和廣播操作。
- Web-bzz:swarm協議相關,分佈式存儲
- web3-utils:Dapp 開發輔助功能
Web3版本說明
- npm ls web3,使用該命令查看版本
- npm update web3,如果版本過低,請升級
版本文檔以此爲準:
https://web3.js.readthedocs.io/en/1.0/web3-eth.html
Web3初始化
// 引入web3
// in node.js use: var Web3=require('web3');
//如果瀏覽器按照了MetaMask,則會提供一個默認的web3.currentProvider
//如果爲空,則連接遠程/本地節點
var web3 = new Web3(Web3.givenProvider || "ws://localhost:8545");
Web3常用API
web3.setProvider,設置Provider
- 參數:無
- 返回值:無
- 示例:web3.setProvider(new web3.providers.HttpProvider(‘http://localhost:8545’));
web3.toWei
- 按對應貨幣轉爲以wei爲單位。最常用的單位爲ether。
- 示例:var value = web3.toWei(‘1’,‘ether’);
console.log(value); // “10000000000000000000”
web3.eth.account,以太坊賬號
- 示例:web3.eth.getAccounts([callback])
web3.eth.getBalance(address[,defaultBlock] [,callback])
web3.eth.contract, 創建一個Solidity的合約對象,用來在某個地址上初始化合約。
- 參數:Array,一到多個描述合約的參數,事件的API對象。
- 返回值:Object,一個合約對象。
- 示例:var MyContract = new web3.eth.Contract(abiArray);
合約對象的方法
- 顯示對象call。myContract.methods.myMethod([param1[,param2[,…]]]).call(options[,callback]);(不修改數據)
- 顯示調用send。myContract.methods.myMethod([param1[,params[,…]]]).send(options[,callback]);(修改數據,消耗gas)
合約調用方法代碼示例:
//合約實例
var contract = new web3.eth.Contract(abi,address);
//callback
contract.methods.helloWorld().call(function(error,result){
colsole.log(result);
});
//promise
contract.methods.helloWorld().call().then(
function(result)(
console.log(result);
)
);
合約對象的事件
- 參數:
-
- Object, 你想返回的索引值(過濾哪些日誌)。如{‘valueA’,1,‘valueB’:[myFirstAddress,mySecondAddress]}。默認情況下,所有過濾項被設置爲null。意味着默認匹配的是合約所有的日誌。
-
- OBject,附加的過濾選項。參見web3.eth.filter的第一個參數。默認情況下,這個對象會設置address爲當前合約地址,同時第一個主題爲事件的簽名。
-
- Function,(可選)傳入一個回調函數,將立即開始監聽,這樣就不用主動調。
合約對象的事件,回調返回值:
- OBject,事件對象,如下:
-
- address:String,32字節,日誌產生的合約地址
-
- args:Object,事件的參數
-
- blockHash:String,32字節,日誌所在塊的哈希。如果是pending的日誌,則爲null。
-
- blockNumber:Number,日誌所在塊的塊號。如果是pending的日誌,則爲null。
-
- logIndex:Number,日誌在區塊中的序號。如果是pending的日誌,則爲null。
-
- event:String,事件名稱
-
- removed:bool,標識產生事件的這個交易是否被一處(因爲孤塊),或從未生效(被拒絕的交易)。
-
- transactionIndex:Number,產生日誌的交易在區塊中的序號。如果是pending的日誌,則爲null。
-
- transactionHash:String,32字節,產生日誌的交易哈希值。
Web3.js的使用與案例
合約調用的基本流程
- 初始化web3,連接以太坊節點rpc服務,獲得一個provider對象
- 初始化合約的對象。
- 合約對象的provider設置爲已知初始化的web3對象。
- 調用合約
- 監聽合約
合約調用
- 合約調用可以使用call或者send。
- myContract.methods.myMethod([param1[,param2[,…]]]).call/send(options[,callback])
- options可以包括from,gasPrice,gas,value。分別代表調用者地址,gas價格,消耗的最低gas,發送的以太幣數量。
Web3調用合約的例子
var Web3 = require('web3');
console.log(Web3.version);
//設置web3對象
var web3 = new Web3('http://localhost:8545');
var json = require("../build/contracts/Hello.json");
var abi = json["abi"];
var address = "0x91ab99f3983y798cenu9eh49erjj88q3u4rjeqd903q4uytr04";
//合約實例
var contract = new web3.eth.Contract(abi,address);
//callback
contract.methods.helloWorld().call(function(error,result){
console.log(result);
});
Truffle對Web3的封裝
// 1.引用編輯好的合約文件結果
var json = require("./build/contracts/MyContract.json");
// 2.將合約轉爲合約抽象層實例
var contract = require("truffle-contract");
var MyContract = contract(json);
// 3. 設置合約抽象層實例的web3 provider
MyContract.setProvider(new Web3.providers.HttpProvider("http://localhost:8545"));
// 4. 開始使用
MyContract.deployed().then(function(deployed){
return deployed.someFunction();
});
Truffle封裝web3的優點
- 對以太坊的智能合約做了更好的抽象,使用簡單
- 同步的交易:可以確保在交易生效之後再繼續執行其他操作。
- 返回Promise:每個封裝的合約函數會返回Promise,可以對它進行.then操作,避免了回調地獄(callback hell)問題。
- 爲交易提供了默認參數:例如from或gas。