【Ethereum 智能合約開發筆記】使用 MetaMask, web3 和 EthJS 呼叫合約
一起來輕鬆開發 Dapp!
使用 web3 JavaScript API(簡稱 web3) 應該是開發 DApp 必須的,不管要查詢 Ethereum 區塊鏈狀態、發送交易、呼叫智能合約都可以透過 web3。使用 web3 必須連結到 Ethereum 節點,之前我寫了一篇使用 Infura 提供的節點:
其實常用的 Ethereum 瀏覽器錢包 — MetaMask 也有提供 web3 provider,用他提供的 provider 初始化 web3,就可以連上 MetaMask 提供的節點。參考以下兩篇官方文件,我實作了簡單的範例,並記錄幾個可能碰到的問題。
MetaMask Compatibility Guide
Developing for MetaMask
另外 MetaMask/mascara 提供在不安裝 MetaMask 的環境下,使用 MetaMask 提供的 web3。
使用範例
寫一個簡單的 JavaScript 程式,使用 MetaMask 提供的 web3 provider 來初始化 web3:
var Web3 = require('web3');// set the provider of web3if (typeof web3 !== 'undefined') { console.debug(web3.currentProvider); web3 = new Web3(web3.currentProvider);} else { alert("No currentProvider for web3");
}
用 browserify 打包:
browserify web3_init.js -o web3_bundle.js
在 HTML file 中執行:
<script src="js/web3_bundle.js"></script>
再寫個 HTML file 測試看看。希望透過 MetaMask 提供的 web3 取得:
- web3 的 API version
- 我的 MetaMask account
<html> <body> <h2>Web3 API version</h2> <p id="p1"></p> <h2>My Account</h2> <p id="account"></p> <script src="js/web3_bundle.js"></script> <script> // Get API version var p1 = document.getElementById("p1"); p1.innerHTML = web3.version.api; // Get my MetaMask account var account = document.getElementById("account"); account.innerHTML = web3.eth.accounts; </body></html>
執行結果:
可能碰到的問題
1) 找不到 web3.currentProvider
- 必須使用 http server,根據 MetaMask 官方文件:
Due to browser security restrictions, we can’t communicate with dapps running on
file://
. Please use a local server for development.
- 必須確認啟用 MetaMask extension。
2) 無法取得 web3.eth.accounts
必須用密碼解鎖 MetaMask,不然會回傳 undefined
。
3) 沒有使用 callback
MetaMask 官方文件表示所有提供的 web3 API 都是非同步,必須要傳入 callback function,除了以下例外:
eth_accounts
(web3.eth.accounts
)eth_coinbase
(web3.eth.coinbase
)eth_uninstallFilter
(web3.eth.uninstallFilter
)web3.eth.reset
(uninstalls all filters)net_version
(web3.version.network
)
除了以上 API,我在使用時也有其他 API 不需要 callback。但確實碰到 API 是必需要用 callback,不然 MetaMask 會跳出 error。
4) Web3 API 版本
以上範例是使用 官方 wiki 的 API,版本是 0.2x.x。如果直接用 npm install web3
,根據我的經驗會安裝的版本為 1.0.0,API 使用的方法會有些不同,使用方式請看 web3.js Doc。
使用 EthJS 呼叫合約
EthJS 是另一個 Ethereum 的 JavaScript API,也是 MetaMask 開發者推薦的 JavaScript API。根據 EthJS 官方文件的描述:
EthJS is a highly optimised, light-weight JS utility for Ethereum based on
web3.js
, but lighter, async only and usingBN.js
.
EthJS 分為多個 module,如果要使用合約要安裝這兩個:
npm install ethjs-query ethjs-contract --save
同樣使用 MetaMask 提供的 web3 provider 來初始化 EthJS:
var Eth = require('ethjs-query');var EthContract = require('ethjs-contract');if (typeof web3 !== 'undefined') { eth = new Eth(web3.currentProvider); contract = new EthContract(eth); startApp();} else { alert("No currentProvider for web3");}
使用 contract
初始化合約,一樣需要合約的 ABI 和 address:
function startApp() { const abi = [ { "constant": true, "inputs": [], "name": "data", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "_from", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Set", "type": "event" }, { "constant": false, "inputs": [ { "name": "x", "type": "uint256" } ], "name": "set", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" } ]; const addresss = '0x06e1c13546e04514a0cf8d842216a84745ac317a'; const SimpleStorage = contract(abi); const simpleStorage = SimpleStorage.at(addresss); // Listen to clicks from a <button> that trigger a function call of contract
listenForClicks(simpleStorage);}
寫一個簡單的 HTML file,有一個 input 欄位可以輸入任意數值,和一個 button。
<input id="data-value" type="text" placeholder="Enter a number"><button class="set">Set Data!</button>
在寫一個 JavaScript function 監聽這個 button,按下 button 後會透過 EthJS 呼叫合約的 set(uint256)
,把合約中的狀態 data
設為對應數值。要呼叫合約不需要知道 function signature,也不用自己建 transaction。就像使用 JavaScript 物件中的 function,像是: simpleStorage.set(param, {from: myAddr}, callback() {...})
。
function listenForClicks(simpleStorage) { var button = document.querySelector('button.set'); button.addEventListener('click', function() { var value = document.getElementById('data-value').value; simpleStorage.set(value, { from: "0x123abc000..." }, function(error, result) { if (error) { console.debug(error); return; } // will return txHash as result console.debug(result); }) })}
用看看
1. 任意輸入一個數字後,按 Set Data!
2. 跳出 MetaMask 的提醒視窗。MetaMask 提供介面讓使用者授權交易的發送,點擊 confirm 就可以發送這筆交易。
3. 發送成功,取得 Transaction Hash。
4. 等 transaction confirm 後,再去呼叫合約的 data()
,就會得到更新後的值。一樣可以透過 EthJS,像是這段簡單的 code:
simpleStorage.data(function(error, result) { // result[0] is a object of bn.js console.debug(result[0].toNumber());})// Return value20