Solidity Hot Reloading Using ZepKit

# Solidityホットリロード
ある日の朝、社内掲示板にて。

　社長「キター！」
　俺「？」

…という事で何がキタのか調べるべく[「Solidity Hot Reloading Using ZepKit」](https://blog.zeppelinos.org/solidity-hot-reloading-using-zepkit/)の記事内容を試してみました。


# Hot Reloading って何？
どうやらようやくsolidity界隈にも Hot Reloading 機能が実装されたようです。

ホットリロードとは「ファイルの変更をトリガにリアルタイムで変更を反映してくれる」機能の事です。
（[こちら](https://qiita.com/karuhi/items/e2056d35a42d49081832)からの引用）

[この記事](https://vue-loader-v14.vuejs.org/ja/features/hot-reload.html)のGIF画像が分かり易いです ↓
![hoge](http://blog.evanyou.me/images/vue-hot.gif)
　　※右のソースコードを書き換える　→　左のブラウザ画面が自動的に更新される　→　便利！


# Solidityのホットリロードって何？
では、Solidityのホットリロードとは何なのでしょうか。

> "引用"
> Hot Reloading watches the .sol files you’re using. Once there’s a change, it compiles them, updates the local blockchain using ZeppelinOS, and refreshes your app in the browser while preserving your blockchain state.
> (ホットリロードは、使用している.solファイルを監視します。 変更があると、それらをコンパイルし、ZeppelinOSを使用してローカルブロックチェーンを更新し、ブロックチェーンの状態を維持しながらブラウザでアプリケーションを更新します。)

じゃあ、ホットリロードを使って何が嬉しいのでしょうか？

> "引用"
> Avoiding endless cycles of building and reloading your project whenever you make a change saves time and focus.
> (変更を加えるたびにプロジェクトを構築して再ロードするという無限のサイクルを回避することで、時間と集中力を節約できます。)

これって大切ですね。
チームで開発していると、意外とこういったルーチンワークが、生産性向上を阻害するボトルネックになっていたりするものだと思います。

> ″引用”
> Yet in the world of blockchain development, we’re 10 years behind.
> ブロックチェーン開発の世界では、10年遅れています。

Solidity Hot Reloading を使えば、10年の遅れを巻き返す事が出来るのか。
さっそく見て行きましょう。


# ​​How to start?
[「Solidity Hot Reloading Using ZepKit」](https://blog.zeppelinos.org/solidity-hot-reloading-using-zepkit/)の真ん中あたりからチュートリアルは始まります。
ここでは、分かり辛い部分に補足を加えて解説していきたいと思います。

なお今回は、Windows10 node.js 環境、Windows Power Shell + Chrome + MetaMask で動作を確認しました。


## Install requirements

まず、必要なnpmパッケージをインストールします。

```console:コマンド
npm install --g zos@2.2.0 ganache-cli@6.3.0 truffle@5.0.2
```
　[zos](https://www.npmjs.com/package/zos)　…　ZeppelinOS Command-Line Interface. Hot Reloadingで必要です。
　[ganache-cli](https://www.npmjs.com/package/ganache-cli)　…　このチュートリアルは、ganache-cli をテストネットとして使います。
　[truffle](https://www.npmjs.com/package/truffle)　…　unboxに必要なだけ（おそらく）。


## Unbox Zepkit
truffleのunboxコマンドを使い、[zepkit](https://zepkit.zeppelinos.org/)をクローンします。
なお、unbox するといきなりファイル・フォルダが展開されるので、作業用フォルダを作成してから行いましょう。

```console:コマンド
mkdir zepkittest
cd zepkittest

truffle unbox zeppelinos/zepkit
```


## Run the local blockchain
ganache-cliを起動します。
コンソールを**もう一つ開いて**、コマンドを実行します。

```console:コマンド
ganache-cli --secure -u 0 -u 1 -u 2 -d
```

```console:実行結果
PS C:\Users\tobisako> ganache-cli --secure -u 0 -u 1 -u 2 -d
Ganache CLI v6.3.0 (ganache-core: 2.4.0)

Available Accounts
==================
(0) 0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1 (~100 ETH)
(1) 0xffcf8fdee72ac11b5c542428b35eef5769c409f0 (~100 ETH)
(2) 0x22d491bde2303f2f43325b2108d26f1eaba1e32b (~100 ETH)
(3) 0xe11ba2b4d45eaed5996cd0823791e0c93114882d (~100 ETH) 🔒
(4) 0xd03ea8624c8c5987235048901fb614fdca89b117 (~100 ETH) 🔒
(5) 0x95ced938f7991cd0dfcb48f0a06a40fa1af46ebc (~100 ETH) 🔒
(6) 0x3e5e9111ae8eb78fe1cc3bb8915d5d461f3ef9a9 (~100 ETH) 🔒
(7) 0x28a8746e75304c0780e011bed21c72cd78cd535e (~100 ETH) 🔒
(8) 0xaca94ef8bd5ffee41947b4585a84bda5a3d3da6e (~100 ETH) 🔒
(9) 0x1df62f291b2e969fb0849d99d9ce41e2f137006e (~100 ETH) 🔒
　　：
```

「--secure」オプションは、
ganache-cliが用意するテストアカウント（全て）をロック状態にします。
（※このチュートリアルでは不要かもです。）

「-u」オプションは、
逆に指定アカウントをアンロック状態にします。
「-u 0 -u 1 -u 2」と書く事で、アカウント「(0)」「(1)」「(2)」がアンロックされます。
（実行結果では、(0)・(1)・(2)に鍵アイコンが無くアンロックされている事が分かります。）
このチュートリアルでは、後述の「zos session」で、アカウント「(2)」がアンロックされている必要があります（※このチュートリアルでは３つもアンロックしなくても…です）。

「-d」オプションは、
ganache-cliが用意するテストアカウントを生成する際のニーモニック（MNEMONIC）を指定するものです。
　１・指定無しの場合は、ganache-cli起動毎にテストアカウントのアドレスが変わります（不便かもです）。
　２・「-d」のみ指定すると、常に同じテストアカウントが生成されます。
　３・「-d -db [PATH]」と指定する事で、ニーモニックをファイル指定する事も出来る様です。

上記「２」で生成されるテストアカウントのアドレスは、別環境でも常に同じです。
（Windows10 と Ubuntu(Cloud9) で試したが同じだった）
このチュートリアルでは、後述の「zos session」でアカウント「(2)」のアドレスが直指定されており、
ここで「-d」を指定する事で整合性が取れています。（なんだかアクロバティックですね）

※ganache-cliのコマンドライン引数の解説は[github](https://github.com/trufflesuite/ganache-cli)で確認して下さい。
※ニーモニックについては、`HD Wallet`でググッって見てください。


## Initialize the ZeppelinOS project
「truffle unbox zeppelinos/zepkit」で取得したチュートリアル・サンプルソースを設定していきましょう。

```console:コマンド
zos init zepkit
```

すると、ファイル zos.json が生成されます。

```json:zos.json
{
  "zosversion": "2.2",
  "name": "zepkit",
  "version": "0.1.0",
  "contracts": {}
}
```


## Add the Counter contract to your project
Hot Reloading で監視するコントラクトを設定します。

```console:コマンド
zos add Counter
```
　すると、下記の様に soliddity ソースファイルがコンパイルされ、zos.json が更新されます。
　![con03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/18801d9b-1640-cd97-aaf0-9d61299ee3a6.png)

ファイル zos.json の "contracts" に、 Counter が書き加えられました。

```json:zos.json
{
  "zosversion": "2.2",
  "name": "zepkit",
  "version": "0.1.0",
  "contracts": {
    "Counter": "Counter"
  }
}
```


## Connect with your local blockchain by opening a session
次に、Hot Reloading で使用するセッション管理情報を保存します。

```console:
zos session --network development --from 0x22d491bde2303f2f43325b2108d26f1eaba1e32b --expires 3600
```

すると、ファイル .zos.session が生成されます。

```json:.zos.session
{
  "network": "development",
  "from": "0x22d491bde2303f2f43325b2108d26f1eaba1e32b",
  "expires": "2019-05-18T15:32:43.175Z"
}
```


## Let’s compile the Counter contract
まだまだ準備は続きます。以下のコマンドを実行し、セッション情報をブロックチェーンにプッシュします。

```console:コマンド
zos push --deploy-dependencies
```

このコマンドを実行したら、ganache-cli の表示にも注目しましょう。

・zosコマンド画面
![con04_zof.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/fb8330c4-3340-a59a-a999-4d69edd822a1.png)
・ganache-cli画面
![con04_ganache-cli.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/bd0e9a37-f5cf-6308-9c84-a3ec11d132b6.png)


## We create an instance of our contract and deploy it
次のコマンドで、ようやくコントラクトがデプロイされインスタンスが生成されます。

```console:コマンド
zos create Counter --init initialize --args 2
```
　※「--args 2」を指定する事で、`Counter.sol`のコンストラクタに引数 2 を渡しています。


## Open another terminal in the `client` folder and run
このチュートリアルでは、MetaMask を ganache-cli に接続して使用するので、まず先に MetaMask のネットワークを「Localhost 8545」に切り替えます。
![meta01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/41e36dd9-745c-1800-9e63-884eab05875d.png)

次に、クライアントweb画面を起動します。
チュートリアルでは別Windowsを開くと書いていますが、ここではそのままコマンドを実行します。

「truffle unbox zeppelinos/zepkit」で取得したフォルダ「client」にパスを移動して、コマンドを実行します。

```console:コマンド
cd client
npm run start
```

するとブラウザが起動し、暫くするとweb画面が表示されます。
（コンパイル後に表示されるので、少し時間がかかります）
![meta_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/ca57b69f-72b3-5e39-3dfd-eb2abe608518.png)

このタイミングで MetaMask の確認画面が出てくるので、「Connect]ボタンを押して許可しておきましょう。
![meta_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/b5836bcc-6017-3851-8c20-0e04dacdaae3.png)

では、web画面を確認したら、そっと閉じて次のURLを開きましょう。
[http://localhost:3000/counter](http://localhost:3000/counter)

こっちが目的のweb画面です。
![web_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/5e7aae99-3bea-8e23-07fc-b7219a5fadaa.png)

さて、ホットリロードを確認する前に、以下をチェックしておきましょう。
![web_02_b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/ac5847ef-f364-6925-c0d9-4bc14f81f6cc.png)
　　「Increase Counter by 1」ボタンはアクティブですが、
　　「Decrease Counter by 1」ボタンは非アクティブで押せない状態になっています。

ここで使用されているSolidityファイルの覗いてみましょう。

```JavaScript:contracts/Counter.sol（修正前）
    :
contract Counter is Initializable {
    :

  //and it can add to a count
  function increaseCounter(uint256 amount) public {
    count = count + amount;
  }

  //We'll upgrade the contract with this function after deploying it
  //Function to decrease the counter
  // function decreaseCounter(uint256 amount) public returns (bool) {
  //   require(count > amount, "Cannot be lower than 0");
  //   count = count - amount;
  //   return true;
  // }
}
```
※関数「increaseCounter()」と「decreaseCounter()」がありますが、後者はコメントアウトされています。

次に、webクライアント側のソースファイル index.js を覗いてみましょう。

```JavaScript:client/src/index.js
    :
        <div className={styles.buttons}>
          <Button
            onClick={() => this.props.increase(1)}
            size="small">Increase Counter by 1</Button>
          <Button
            onClick={() => this.props.decrease(1)}
            disabled={!contract.methods.decreaseCounter} size="small">Decrease Counter by 1</Button>
        </div>
    :
```
※「**disabled={!contract.methods.decreaseCounter}**」で、コンパイルされたSolidityソースにメソッド「decreaseCounter()」が含まれているかを確認し、ボタンのアクティブ・非アクティブを切り替えている事がわかります。

今回のお題は「ホット・リローディング」ですので、
Counter.sol の関数「decreaseCounter()」コメントを外し有効にして、ファイルを保存する事で自動的にホット・リロードすれば成功、という事になります。

それでは、やってみましょう。

下記の様に、「Counter.sol」の関数「decreaseCounter()」のコメントを外します。

```JavaScript:contracts/Counter.sol（修正後）
    :
contract Counter is Initializable {
    :

  //and it can add to a count
  function increaseCounter(uint256 amount) public {
    count = count + amount;
  }

  //We'll upgrade the contract with this function after deploying it
  //Function to decrease the counter
  function decreaseCounter(uint256 amount) public returns (bool) {
    require(count > amount, "Cannot be lower than 0");
    count = count - amount;
    return true;
  }
}
```

ファイルを保存すると、自動的にコンパイルが始まります。
![auto_compile.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/63c69d49-0426-9905-8141-919524ce9260.png)

そして、内容に変更があった場合のみ、自動的にデプロイが行われます。
（今回は関数「decreaseCounter()」が新たに加わった）
![auto_compile_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/73a75d14-2885-5bf1-3635-1054325bda21.png)

さらに、web画面も自動的に更新され、
![OK.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/4fc05725-3aaa-9bce-158a-02766872fbfa.png)
　　ソースファイルを保存しただけで、
　　上記の様に「Decrease Counter by 1」ボタンがアクティブとなり、押せる様になりました。

これで、Solidity の Hot Reloading がうまく動作した事が確認できました。


## 失敗するケース
サクサクやらないと失敗するかもしれません。以下の点に注意しましょう。
　・1時間(3600秒)以内に終わらせる。
　・失敗したら、ganache-cli を一度再起動して、「zos init zepkit」からやり直す。
　・たまに「zos.json」（およびzos-dev-xxxx.json）を手動で消さないとやり直し出来ない事がある。


# せっかくなのでボタンを押してみよう
ここからはチュートリアルには無いオマケです。

ボタンを押すと、コントラクト関数を呼び出して、カウンターを増やしたり減らしたり出来る筈ですが、
関数呼び出しには gas が必要です。

もし ether 残高が足りないと、MetaMask で「残高不足」となってしまいます。
![gas_not_enough.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/541cfb44-4b78-7c70-ce39-387d3b4abaa7.png)

ganache-cli を使ったテストネットなので、MetaMask で TEST-ether を使う必要があります。
　１・使用している MetaMask に ether を送ってやる
　２・MetaMask にテストアカウントをインポートしてやる

MetaMask にテストアカウントを追加するのはちょっとアレな感じなので、
ここでは「１」の方法で、geth コンソールを起動して、ganache-cliテストネットに接続し、MetaMask のアカウントに ether を送金しましょう。

```console:コマンド
geth attach http://127.0.0.1:8545
```

ganache-cli に geth コンソールで接続出来たら、下記の送金コマンドを実行します。
（「to:」のアドレスは、自分の MetaMask アカウントに置き換えてください。）

```console:gethコンソール・コマンド
eth.sendTransaction({from:eth.accounts[0],
    to:"0xd7436A5d7AabaDE2cdBA5FA9489Afd15af2E86EA",
    value:1000000000000000000})
```
　　※ganache-cli は最初から１０個のテストアカウントが作成され、それぞれ 100 ether ずつ入っています。
　　※上記コマンドでは、アカウント０番→自分の MetaMask アカウントへ 1 ether を送金しています。

これで準備ＯＫです。

さっそく「Increase Counter by 1」ボタンを押してみましょう。
![OK.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/4236e60a-bbe7-eba3-d85f-83ea20a3c43b.png)

ボタンを押すと、MetaMask のポップアップが表示され、今度はちゃんと押す事が出来ます。「確認」ボタンを押しましょう。
![gas_ok.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/ae8e432b-c68a-f279-2b9c-c879c43176ec.png)

　※初期値「2」から、「3」に更新される事が確認できました。
　![count_up.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280104/9c380051-888b-2368-f08a-18c2ad382f81.png)


# 感想
コントラクトの自動デプロイは便利でした。ただ、色々触っているとMetaMaskとの連携でエラーが出たり、ganache-cliを停止させると整合性が保てなくなる等、まだまだ使い勝手としては向上の余地があるのかなと思いました。zosの今後の進歩に期待です。


# FLOCブロックチェーン大学校
[FLOCブロックチェーン大学校・スマートコントラクト開発課](https://floc.jp/course/engsmart/)は、solidity・truffleを学ぶ事が出来るカリキュラムです。web受講の他に教室受講オプションがあり、講師に直接質問出来て身に付きやすいです。短期集中で ethereum 開発ノウハウを獲得するならおススメです。

### ちょっと嬉しいと思った事
5月下旬からの回では、講師・講師補助メンバーとして関わって下さる方々が増えるのですが、
エンジニア同士でカリキュラムの進め方についてあーだこーだ議論出来るのが嬉しいです(*´Д｀)
きっと違った目線からより面白いワークショップが生まれるんじゃないかと思うと今から楽しみです。
乞うご期待！