MetaMaskによるログイン機能の実装

# はじめに

DAppsの開発を始めて最初に悩むのが「ログイン機能をどう実装するか？」だと思います。
DAppsであるからには、ログインIDはウォレットのアドレスであるべきだと考えるのが自然でしょう。
ですが、ウォレットのアドレスは人間が覚えておくようには設計されていません。
では、DAppsのログイン機能はどう実装するべきなのでしょうか？
ここでは、その問題に対する解決策の一つとして、ウォレットの一種であるMetaMaskと連携したログイン機能の実装例を紹介したいと思います。

※MetaMaskは数あるウォレットの中でもリスクの大きいウォレットに分類されます。
ユーザーの中には大事なウォレットアドレスをMetaMaskと紐付けることに抵抗感を覚える人もいるということを踏まえた上で採用を検討してください。

# MetaMask: web3 will be deprecated?

現在、MetaMaskとの連携でよく紹介されているのが「web3」オブジェクトを利用した方法です。
ですが、この方法は将来的に廃止されることが決まっており、既にGoogle Chromeでは「deprecated(非推奨)」となっています。
そのため、ここでは「web3」オブジェクトの代わりに「ethereum」オブジェクトを利用して実装してみたいと思います。
（ちなみに「ethereum」オブジェクトは「web3.currentProvider」と等価です。）

# ethereumによるログイン機能の実装

ここでは以下のサイトを参考にログイン機能を実装していきます。

Ethereum Provider API - MetaMask Docs
https://docs.metamask.io/guide/ethereum-provider.html

「ethereum」オブジェクトはGoogle Chrome拡張であるMetaMaskによってDAppsに注入されますので、Google Chromeブラウザを使用してMetaMaskをインストールしていることが大前提になります。
そのため、最初の分岐条件は以下のようになります。

```javascript
if (!window.ethereum || !window.ethereum.isMetaMask) {
  alert('MetaMaskをインストールしてください。')
  return
}
```

「ethereum.isMetaMask」がMetaMaskをインストールしているかを表すプロパティなのですが、MetaMaskをインストールしていないと、そもそもethereumオブジェクトが注入されないので、ここではethereumオブジェクトの存在チェックも行っています。
MetaMaskがインストールされていたら、次はログイン状態を確認します。

```javascript
window.ethereum.request({method: 'eth_accounts'}).then(accounts => {
  if (accounts.length > 0) {
    // ログイン済み
  } else {
    // 未ログイン
  }
})
```

複数アカウントに同時にログインしている場合があるのか謎ですが、確認方法としてはログイン中のアカウント一覧を取得して一覧の件数が1件以上であればログインしていると判定します。

## ログイン済みの場合

接続中のネットワークの種類を確認します。ネットワークの種類が異なる場合はネットワークの切り替えを促します。
また、ネットワークの種類が一致する場合はログイン中のアカウント一覧の最初の要素をログインIDとします。

```javascript
if (window.ethereum.chainId != 0x1) {
  alert('メインネットに切り替えてください。')
} else {
  this.loginId = accounts[0]
}
```

尚、ネットワークの種類は以下の通りです。

|値|ネットワークの種類|
|:-----------|:------------|
|0x1|メインネット|
|0x3|Ropstenテストネット|
|0x4|Rinkebyテストネット|
|0x5|Goerliテストネット|
|0x2a|Kovanテストネット|

## 未ログインの場合

ログインを促すダイアログを表示します。

![MetaMask Login](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/291442/8c9316c1-94a7-7059-182c-23d878931145.png)

```javascript
window.ethereum.request({method: 'eth_requestAccounts'}).then(requestAccounts => {
  if (requestAccounts.length > 0) {
    if (window.ethereum.chainId != 0x1) {
      alert('メインネットに切り替えてください。')
    } else {
      window.location.reload()
    }
  } else {
    alert('MetaMaskにログインしてください。')
  }
})
```

ethereum.requestメソッドの戻り値はPromiseオブジェクトのため、ユーザーがMetaMaskにログインするとthen以降の処理が実行されます。
無事にログインが成功した場合、通常のWebアプリケーションであればログイン画面からメイン画面への遷移が発生しますが、MetaMaskでログインした場合は、画面を再読み込みするのが良いでしょう。

# ログアウト機能の実装

ここまででログイン機能については実装できましたので、次にログアウト機能を実装します。
ログアウトはアカウント情報の変更イベントをリッスンすることで実装することができます。

```javascript
window.ethereum.on('accountsChanged', changedAccounts => {
  if (changedAccounts.length == 0) {
    // ログアウト
    window.location.reload()
  } else {
    // アカウント変更
  }
})
```

ここではログイン中のアカウント一覧が0件になった（＝ログアウトした）場合のみ、画面を再読み込みしていますが、DAppsによってはアカウントが変更になった場合も、画面を再読み込みすることによって「ログアウト→別アカウントでログイン」を実現できると思います。
また、DAppsによってはネットワークの種類が変更された場合もログアウトと判定する必要がありますが、これも先ほどと同じ要領で実装できます。

```javascript
window.ethereum.on('chainChanged', changedChainId => {
  if (changedChainId != 0x1) {
    window.location.reload()
  }
})
```

これでMetaMaskと連携したログイン／ログアウトの機能が実装できました。
最初にも少し触れた通り、「ethereum」オブジェクトは「web3.currentProvider」と等価ですので、ログイン機能だけではなく、イーサリアムや独自トークンの送金等も可能です。
この記事が少しでも皆さんのDApps開発のお役に立てれば幸いです。

# 最後に
モバイル版MetaMaskの正式リリースが待ち遠しい～！！