Nuxt.jsでEthereumアプリ開発 - pluginでコントラクトと接続する

# はじめに
この記事は、Nuxt.jsを使ってEtherumのブロックチェーン上で動くアプリを開発しようという人向けにはじめの一歩として書きました。前回の記事では、ブロックチェーンとの接続について書きましたが、今回はスマートコントラクトとの通信までできるように実装を進めます。スマートコントラクトは、ブロックチェーンの台帳上にプログラム本体をおき、ブロックチェン上のVMの上で動くプログラムです。ブロックチェーンの読み書きの間に入るインターフェース層に当たるものです。

# 前提条件
[Nuxt.jsでEthereumアプリ開発(pluginでチェーンと接続する)](https://qiita.com/aramsan/items/93e35a27e6fb51a2f269)が動く環境を用意してください。
また、ブロックチェーンはGanacheで作られたテスト用のチェーンを想定して書いてあります。開発中は
Ganacheを起動して、QuickStartボタンを押してそのまま放置しておけばOKです。

#Truffleの追加
スマートコントラクトをコンパイルし、ブロックチェーンにデプロイするために使うツールTruffleをインストールします。
スマートコントラクトって何？って方は[こちら](https://gaiax-blockchain.com/smart-contract)を読んでいただけると嬉しいです。

```
$ yarn add truffle --dev
```
インストールされたら、スマートコントラクトを開発する環境を作ります。

 ```
$ yarn truffle init
```

するといくつかのbuild,contracts,migrationsなどのディレクトリとtruffle-config.jsが生成されます。
これらのディレクトリにコントラクト本体や、デプロイスクリプトなどを追加していきます。

# スマートコントラクトの追加

まずは、動作させるためののスマートコントラクトを書きます。

```contracts/SingleNumRegister.sol
pragma solidity ^0.5.12;
contract SingleNumRegister {
    uint storedData;
    function set(uint x) public{
        storedData = x;
    }
    function get() public view returns (uint retVal){
        return storedData;
    }
}
```
スマートコントラクトコードはコードは、Ethereum入門の「[スマートコントラクトを作成し実行する](https://book.ethereum-jp.net/first_use/contract.html)」から拝借してきました。このスマートコントラクトは、set()という関数に引数を与えると、その引数をコントラクト内の変数storedDataに保存し、get()という関数で保存された値を返すという単純な機能を持っています。

書き上がったら、コンパイルしてみましょう。

```
$ yarn truffle build
```

きっとビルドできると思います
ビルドに成功したら、build/contractsディレクトリに以下に「SingleNumRegister.json」というファイルができていると思います。ここのjsonファイルの中にコンパイルされバイナリデータになったコントラクトや、Nuxtなどのソフトウェアから触るために必要な関数やデプロイ時のデータなどが保存されていきます。

次に、スマートコントラクトをブロックチェーンにデプロイするためのスクリプトを書きます。

```javascript:migrations/2_deploy_contract.js
const SingleNumRegister = artifacts.require("SingleNumRegister");

module.exports = function(deployer) {
  deployer.deploy(SingleNumRegister);
};
```
SingleNumRegister.jsonを使ってブロックチェーンにデプロイする流れになっています。

次に、デプロイするブロックチェーンの設定をします。

```javascript:truffle-config.js
  networks: {
    ・・・
    //development: {
    //  host: "127.0.0.1",     // Localhost (default: none)
    //  port: 8545,            // Standard Ethereum port (default: none)
    //  network_id: "*",       // Any network (default: none)
    //},
```

truffule-config.jsのこの部分のコメントアウトを外し、ポートをGanacheのデフォルトである7545に変更しておきます。

```javascript:truffle-config.js
  networks: {
    development: {
      host: "127.0.0.1",     // Localhost (default: none)
      port: 7545,            // Standard Ethereum port (default: none)
      network_id: "*",       // Any network (default: none)
    },
```

これで、ブロックチェーンにスマートコントラクトをデプロイする準備ができました。
では、早速ブロックチェーンにデプロイしてみましょう！

```
$ yarn truffle deproy
・・・
✨  Done in X.XXs.
```

✨ Doneと表示されれば成功です！

# MetaMaskを使ってブロックチェーンと接続する

この後、ブロックチェーンに対して書き込みを行うことになります。この時、書き込み手数料であるGAS代となるETHの支払いと、署名をするための秘密鍵とアカウントが必要になります。これらをブラウザを通じて、アプリケーションと橋渡しを、MetaMaskがやっています。

### MetaMaskのインストール

まずブラウザとして、Chrome選び、機能拡張からMetaMaskをインストールしましょう。ブラウザでMetaMaskで検索して最初に出てくるものがおそらくChrome Storeでしょう。そこからインストールしてもらえればOKです。

### アカウントの追加

次に、アカウントの作成とインポートを行います。「Get Started」ボタンを押し、「Create a Wallet」を選択。デバッグのためのデータ収集には同意しても拒否してもどちらでもOKです。その後パスワードを設定して、シードの保存をするのですが一旦無視しても構いません(本当はちゃんと保存しないとダメですよ)。すると新しいカウントが表示されます。

しかし、今回は、Ganacheで作ったアカウントを使用するために、一旦別のアカウントを作ります。

まず、Ganacheの管理画面に表示されるアカウント一覧の一番上のアカウントの右側の鍵のアイコンをクリックしてください。すると、アドレスとプライベートキーが表示されます。このプライベートキーをコピーしましょう。

そして、MetaMaskに戻り、右上の丸いアイコンをクリックして、表示されたメニューから「アカウントの作成」を選択します。そこで表示されたダイアログの「追加」タブを選び、先程コピーした秘密鍵を貼り付けてください。これで、ポチポチすればアカウント作成はOKです。

### ネットワークの設定

次に、どのブロックチェーンに接続するかの設定を行います。現在接続されているのはEthereumのメインネットワークです。それが表示されている部分をクリックすると、ネットワークの選択画面に移ります。しかし、Ganacheのデフォルトで作られる、localhost:7545の設定は用意されてません。よて、カスタムRPCで設定します。

ネットワーク名に「Ganache」、RPC URLに「 http://localhost:7545 」と入れましょう。後は保存ボタンを押せば、Ganacheのブロックチェーンへの接続に切り替わります。うまく行けば手持ちが約100ETHと表示されます。デフォルトでは100ETH配られているのですが、スマートコントラクトをデプロイするときに少し使った分が減っています。とはいえ、100ETHって結構な大金(2019年12月現在)なのでちょっとリッチになった気分になれますね。

# Nuxtでコントラクトと接続する

ここまできて、やっと本題に入れます。Nuxt.jsで書かれたアプリからコントラクトへのアクセスを行っていきます。

## MetaMask経由でブロックチェーンに接続

まずは、MetaMaskを使って署名できるように、ブロックチェーンとの接続するためのプラグインを書き換えます。

```javascript:plugins/web3.js
import Web3 from "web3"

export default async function(context, inject) {
    let web3

    if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') {
        web3 = new Web3(window.ethereum)
        window.ethereum.enable().catch(error => {
            // User denied account access
            console.log(error)
        })
    } else if (typeof window !== 'undefined' && typeof window.web3 !== 'undefined') {
        web3 = new Web3(window.web3.currentProvider)
    } else {
        const httpEndpoint = 'http://127.0.0.1:7545'
        web3 = new Web3(new Web3.providers.HttpProvider(httpEndpoint))
    }

    inject('web3',web3)
}
```

このコードの説明をすると長くなってしまうのですが、変更した部分でMetaMaskとの接続部分を実装したと思っていただけたらOKです。

## コントラクトとの接続

次に同じファイルにコントラクトとのインスタンス追加処理も追加しちゃいます。

```javascript:plugins/web3.js
import Web3 from "web3"
import artifacts from "~~/build/contracts/SingleNumRegister.json"// コントラクトのコンパイル後の設定ファイルの読み込み

・・・

    let networkId = await web3.eth.net.getId()// チェーンのネットワークIDを取得
    let contract = new web3.eth.Contract( //コントラクトのインスタンスの初期化。設定ファイルと、アドレスが必要
        artifacts.abi, // コントラクトのコンパイル後の設定ファイル
        artifacts.networks[networkId].address // ネットワークIDごとに保存されているコントラクトのアドレスを読み込む
    )

    inject('web3',web3)
    inject('contract',contract) // インスタンスを生やす
}
```

コード内にコメントを書き込みましたが、この解説通りのことを行っています。
また、今回はコントラクトを1つしか使わない想定で書いてますので、同じ領域を初期化しているweb3と同時にインスタンスを追加する作業を行っています。

## フロントエンドの実装

今回は、数値をセットする機能と、数値を読み込む機能の2つを実装します。

### メソッドの実装

まずは、コントラクトから読み書きするメソッドを実装します。

```javascript:pages/index.vue
・・・
export default {
  data() { 
    return {
      number: 0, // コントラクトから取得する数値
      inputNumber: 0 // フォームから入力された数値
    }
  },
  methods: {
    getNumber: async function() {
      let ret = await this.$contract.methods.get().call() // コントラクトからの読み込み部分
     this.number = ret // フロントへ反映
    },
    setNumber: async function() {
      let accounts = await this.$web3.eth.getAccounts() // MetaMaskで使っているアカウントの取得
      let account = accounts[0]
      let ret = await this.$contract.methods.set(this.inputNumber).send({from: account}) // コントラクトへの書き込み部分
    },
  },
・・・
```
コントラクトの関数とのやりとりは、contractのmethods.[関数名(引数)]でを通じて行います。読み込みだと.call() 書き込みだと .send({from: [書き込みアカウントのアドレス]})で行います。

前半は、読み込み用のメソッドです。単純に、get()関数を.call()を使って叩いているだけです。
後半は、書き込み用のメソッドです。まず。MetaMaskからユーザーのアドレスを取得し、そのアドレスの署名を使って、コントラクトへの数値の書き込みを行います。

### テンプレートの実装

テンプレートには、数値を入力するフォームと書き込むボタン、そして数値を読み込むボタンの2つを設置します。デフォルトで、Githubや、ドキュメントへのリンクのあった場所を書き換えます。

```javascript:pages/index.vue
・・・
      <div class="links">
        <input
          type="text"
          v-model="inputNumber"
          placeholder="input number"
        >
        <button
          @click="setNumber()"
        >
          Set Number to contract
        </button>
      </div>
      <div class="links">
        <button
          @click="getNumber()"
        >
          Get Number from contract
        </button>
      </div>
      <div>
        Number:{{number}}
      </div>
・・・
```

ここまでできれば、実際にブロックチェーンを使ったコントラクトへの読み書きが行なえます。書き込み→読み込みの順で実験してみてください。

書き込み時には、MetaMaskが開き、書き込み手数料の確認とともに署名を行います。読み込みのときはときに何もおきません。

これで、一通り、スマートコントラクトの通信を行うことができました。前準備が長いのですが、一度準備してしまえばコントラクトとの通信部分は簡単に実装できます。これでNuxtでブロックチェーンアプリケーションを開発する敷居が少しは下がり、ちょっとやってみようと思っておらえると幸いです。

ソースコード：https://github.com/gaiax/nuxt-web3-contract