【Ethereum 智能合约开发笔记】使用 MetaMask, web3 和 EthJS 呼叫合约

使用 web3 应该是开发 DApp 必须的,不管要查询 Ethereum 区块链状态、发送交易、呼叫智能合约都可以透过 web3。使用 web3 必须连结到 Ethereum 节点,之前我写了一篇文章介绍使用 Infura 提供的节点。其实常用的 Ethereum 浏览器钱包 — MetaMask 也有提供 web3 provider,用他提供的 provider 初始化 web3,就可以连上 MetaMask 提供的节点。参考以下两篇官方文件,我实作了简单的范例,并记录几个可能碰到的问题。

MetaMask 官方文件:

  1. MetaMask Compatibility Guide
  2. Developing for MetaMask

另外 MetaMask/mascara 提供在不安装 MetaMask 的环境下,使用 MetaMask 提供的 web3。

使用范例

写一个简单的 JavaScript 程式,使用 MetaMask 提供的 web3 provider 来初始化 web3:

var Web3 = require('web3');
// set the provider of web3
if (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 取得:

  1. web3 的 API version
  2. 我的 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 using BN.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!


    输入 20
  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 value
20

References

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容