[web3]Dockerでトークンのデプロイ環境（Mumbai）を構築してみた

# はじめに
web3サービスを展開している企業、どんどん増えてますね！
いまいちよく分からない、という方もたくさんいると思いますが、まずは実際に触れてみる、というところからスタートしましょう:slight_smile:

この記事では、Dockerを使って、Polygonのテストネット（Mumbai）にトークンを発行してみます。

※2023年5月時点の情報です

この記事のソースコードを[Git](https://github.com/gYama/polygon-based-token-deployment)にプッシュしてあるので、こちらも参考にしてください。

# この記事の概要
- Dockerで環境構築をする
- Polygonのテストネット（Mumbai）にトークンをデプロイする
- MetaMaskでトークンを確認する

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

# 事前準備
## Metamaskの用意
### Chromeにインストールする
まずは、トークン発行用のアカウントを用意します。
MetaMaskは仮想通貨のデジタルウォレットで、トークンを受け取ったり、他のウォレットに送信したりすることができます。

Chromeの拡張機能として追加できるので、[公式サイト](https://metamask.io/)から追加しましょう。

### ウォレットの秘密鍵を取得する
追加が完了したら、以下の手順でアカウントを作成して、秘密鍵を控えておいてください。
1. テスト用に、新規アカウントを作成
2. 設定から秘密鍵をコピーして控えておく
秘密鍵は、アカウントの詳細からコピーできます。
※秘密鍵は、自分以外の人に知られないようにしてください。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/cf3b25f7-682c-9b86-6d56-00bb55fa8bf6.png">
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/fd9e3521-52f3-a6f2-44fb-4b6902dacea3.png">

3. Mumbaiネットワークの追加
デフォルトではMumbaiネットワークが選択できないので、手動で追加します。
[Polygonの公式サイト](https://wiki.polygon.technology/docs/develop/metamask/config-polygon-on-metamask/#add-the-polygon-network-manually)で案内しているので、`Add the Polygon network manually`の`Mumbai-Testnet`を選択し、ネットワークを追加しましょう。
MetaMaskで`ネットワークを追加`をクリックして、ネットワーク名、RPC URLなど、案内に従って設定してください。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/27dce29d-f187-3820-2b0c-c564f7f3328d.png">
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/96c07a39-a5e1-d01c-29e9-965b1438bb45.png">
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/a42a0987-88a9-195f-8d5d-78aa27d30d73.png">

## Faucetの取得
トークンをデプロイする際には、ガス代（ネットワークの利用料のようなもの）がかかります。
テストネットでは、少額のトークンをもらうことができ、これはFaucetと呼ばれています。

### MumbaiのFaucetを自分のウォレットで受け取る
以下の手順で、Faucetを受け取りましょう。
1. Polygonの[Faucetサイト](https://faucet.polygon.technology/)にアクセス
2. Networkは`Mumbai`を選択
3. Select Tokenは`MATIC Token`を選択
3. Wallet Addressに自分のウォレットのパブリックアドレスを入力
3. Submitをクリック
3. 確認画面が表示されるので、`Confirm`をクリック
3. Request Submittedが表示されたらOK
3. 1〜2分後にウォレットに届くので、確認しましょう
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/3b643fd8-1f4c-04e1-5c01-50e530699392.png">

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/d3049b84-5931-d557-9b20-2d329746e600.png">

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/2843b000-d970-d518-b7cf-cfed981dfd68.png">

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/c8985cbe-edb9-f160-b3ef-419f1dfdc1bd.png">

正常に受け取れていれば、MATICが増えています。
このMATICを使って、Mumbaiへのデプロイを行います。

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/a427aec3-cb59-8900-aa3a-11cdeccf4ac8.png">

## infuraでAPI Keyを取得
infuraは、ブロックチェーンのネットワーク（例：Ethereum）にアクセスして、その機能を利用できるサービスを提供しています。これにより、開発者は簡単にブロックチェーン技術を使ったアプリケーションを開発することができます。
1. [infra](https://app.infura.io/)でアカウントを作成する
2. 「API Keys」で「CREATE NEW API KEY」をクリック
 a. NETWORKは「Web3 API」を選択する
 b. NAMEを入力
3. 「API Keys」で作成したAPIをクリック。API Keyをコピーして、控えておく
4. EndpointsでPolygonが有効になってない場合は「ACTIVATE ADD ON」をクリック。フリープランを設定する。
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/765c299c-5903-283b-a674-586d4ad32aae.png">

## Dockerのインストール
Docker環境が無い場合は、[Docker](https://www.docker.com/)からインストールしてください。

# Mumbaiネットワークでトークン発行
## 環境構築
### プロジェクトディレクトリの作成
```bash
mkdir polygon-based-token-docker
cd polygon-based-token-docker
```
以降、このディレクトリ配下で作業を行います。
### package.jsonの作成
package.jsonは、システムの依存関係を設定するファイルです。
バージョンを明確にしておくことで、同じ環境を構築することができます。
```json:package.json
{
  "dependencies": {
    "@truffle/hdwallet-provider": "^1.7.0",
    "@openzeppelin/contracts": "^4.8.3",
    "truffle": "^5.4.21"
  },
  "scripts": {
      "develop": "truffle develop --host 0.0.0.0 --port 8545",
      "deploy-mumbai": "truffle migrate --network mumbai"
  }
}
```
Truffleは、ブロックチェーン上で動作するプログラム（スマートコントラクト）を作るためのツールです。これを使うことで、開発者は簡単にスマートコントラクトを書いたり、テストしたり、デプロイ（公開）したりすることができます。

### Dockerfileの作成
すでに用意されたDockerイメージもあるのですが、せっかくなので、Dockerfileから作成しましょう。
プロジェクトディレクトリに`Dockerfile`を作成し、以下の内容を記述します。
```dockerfile
FROM node:14

# 作業用のディレクトリを作成する
RUN mkdir -p /app
WORKDIR /app

# ローカルのファイルを全てコンテナの/appにコピーする
COPY . /app
# package.jsonの設定に従って、依存関係をインストールする
RUN npm install

# package.jsonの"script"に書かれたコマンドを、Mumbaiネットワークで実行する

# デプロイテスト用（トークンが仮想ネットワーク上にデプロイされる）
# CMD ["npm", "run", "develop"]

# Mumbaiへのデプロイ用（トークンがMumbaiネットワークにデプロイされる）
CMD ["npm", "run", "deploy-mumbai"]
```
### .envの作成
プロジェクトディレクトリに`.env`を作成します。
`WALLET_PRIVATE_KEY`には事前準備で作成したMetamask送信用アカウントの秘密鍵を設定します。`API_KEY`には、事前準備で作成したinfuraのAPI Keyを設定します。
```bash:.env
WALLET_PRIVATE_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
API_KEY=xxxxxxxxxxxxxxxxxxxxx
```
### truffle-config.jsの作成
Truffleの設定ファイルを作成します。
```
const HDWalletProvider = require("@truffle/hdwallet-provider");
// 環境変数からウォレットの秘密鍵を取得
const privateKey = process.env.WALLET_PRIVATE_KEY;
// 環境変数からAPIキーを取得
const API_KEY = process.env.API_KEY;

module.exports = {
    networks: {
      develop: {
        host: "127.0.0.1",
        port: 8545,
        network_id: "*",
      },
      mumbai: {
        provider: () => new HDWalletProvider(privateKey, "https://rpc-mumbai.maticvigil.com"),
        network_id: 80001,
        confirmations: 2,
        timeoutBlocks: 200,
        skipDryRun: true,
      },
    },
    compilers: {
        solc: {
          version: "0.8.0",
        },
    }
};
```
### トークンのスマートコントラクト用ファイル
トークンのスマートコントラクト用ファイルを作成します。`contracts`ディレクトリを作成し、`contracts/MyToken.sol`ファイルを実装します。今回は、ERC20トークンの[サンプルコード](https://solidity-by-example.org/app/erc20/)を使用してコントラクトを作成します。
```javascript:contracts/MyToken.sol
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
        _mint(msg.sender, initialSupply);
    }
}
```
### コントラクトのデプロイ用ファイル
`migrations`ディレクトリを作成し、`migrations/2_deploy_contracts.js`ファイルを実装します。
```javascript:migrations/2_deploy_contracts.js
const MyToken = artifacts.require("MyToken");

module.exports = function (deployer) {
  deployer.deploy(MyToken, "1000000000000000000000000"); // 1,000,000 tokens with 18 decimal places
};
```
### イメージのビルド
プロジェクトディレクトリで、以下のコマンドを実行してDockerイメージをビルドします。（ビルドに2分くらいかかります）
何度かやりなおすこともあるので、念のため、コンテナを削除してからビルドします。コンテナが無ければエラーメッセージが表示されますが、気にしなくて大丈夫です。
```bash
docker rm polygon-based-token-container
docker build -t polygon-based-token-deploy .
```
`docker images`コマンドで以下のようにイメージができていたら成功です。
```bash
$ docker images | grep polygon-based-token-deploy
polygon-based-token-deploy   latest    c0760a3a75d9   4 minutes ago   1.37GB
```
### Dockerコンテナの実行
ビルドが完了したら、以下のコマンドでDockerコンテナを実行します。
```bash
docker run -it --env-file .env --name polygon-based-token-deploy-container polygon-based-token-deploy
```
このコマンドでDockerコンテナ内でTruffleが起動し、Mumbaiネットワーク（Polygonのテストネット）へトークンのデプロイが行われます。

デプロイが成功すると、以下のような内容が出力されると思います。
解説をコメントで記載しているので、若干読みにくいかもしれません。ご容赦ください:bow:
```bash
$ docker run -it --env-file .env --name polygon-based-token-deploy-container polygon-based-token-deploy

# deploy-mumbaiの設定を使って
# Mumbaiネットワークへのデプロイコマンドを実行
> @ deploy-mumbai /app
> truffle migrate --network mumbai

# Truffleがソースコードをコンパイルしている
Compiling your contracts...
===========================
> Compiling ./contracts/MyToken.sololc-bin. Attempt #1
> Compiling @openzeppelin/contracts/token/ERC20/ERC20.sol
> Compiling @openzeppelin/contracts/token/ERC20/IERC20.sol
> Compiling @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
> Compiling @openzeppelin/contracts/utils/Context.sol
> Compilation warnings encountered:

# コンパイル中に警告が検出された
# この場合、SPDXライセンス識別子がソースファイルに含まれていないという警告です。
# この警告は、コントラクトの機能に影響を与えませんが、公開前には修正を検討してください。
    Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> project:/contracts/MyToken.sol

# スマートコントラクトをコンパイルして、Mumbaiネットワークに
# デプロイするプロセスが始まっていることを示しています
> Artifacts written to /app/build/contracts
> Compiled successfully using:
   - solc: 0.8.0+commit.c7dfd78e.Emscripten.clang
⠸ Fetching solc version list from solc-bin. Attempt #1
⠴ Fetching solc version list from solc-bin. Attempt #1
Starting migrations.... Attempt #1.
======================
> Network name:    'mumbai'
> Network id:      80001
> Block gas limit: 20000000 (0x1312d00)

# 2_deploy_contracts.jsの実行
2_deploy_contracts.js
=====================
⠦ Fetching solc version list from solc-bin. Attempt #1
   Deploying 'MyToken'. Attempt #1.
   -------------------
# この処理が行われたトランザクションのハッシュ値
   > transaction hash:    0x42a97704f8efaef194de6ecddfd4903e8b7b7cbd31dd533d12c9b06af63336ec
# トランザクションが含まれるブロック数と
# トランザクションが完了するまでにかかった時間
   > Blocks: 2            Seconds: 4lc-bin. Attempt #1
# Mumbaiにデプロイしたコントラクトのアドレス
   > contract address:    0x367d5FA9c8F9875bF4221CA0E25A7362211Ad997
# ブロックチェーン上でのブロックの位置
   > block number:        35257753 4
# ブロックが生成された日時のUNIXタイムスタンプ
   > block timestamp:     1683377580
# トランザクションを送信したアカウントのアドレス
   > account:             0xe6b1af45f7cF652aC02d819228e622EFB1b3Df10
# アカウントの残高
   > balance:             0.189641197915978606
# トランザクション実行に使用されたガス量
   > gas used:            1150978 (0x119002)
# ガスの単価（gwei単位）
   > gas price:           2.500000016 gwei
# トランザクションで送信されたETHの量
   > value sent:          0 ETH
# トランザクションの総コスト（ETH）
   > total cost:          0.002877445018415648 ETH

   Pausing for 2 confirmations...

   -------------------------------
# 確認回数（ブロック承認の数）と確認されたブロックの番号
   > confirmation number: 2 (block: 35257755)ttempt #1
# デプロイメント関連のデータを保存
   > Saving artifactsr. Attempt #1.
   -------------------------------------
# トランザクションの総コスト（ETH）
   > Total cost:     0.002877445018415648 ETH
```
これで、MumbaiにMyTokenコントラクトがデプロイされました。

## MetaMaskで確認
では最後に、MetaMaskでトークンを確認してみましょう。
### トークンをインポート
MetaMaskを起動すると、下部に「トークンをインポート」というリンクがあるので、クリックしてください。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/7b609aab-8d0a-7966-9f80-c3235ea0e0a6.png">
以下の画面が表示されます。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/c3f75dd4-7ab3-8452-1529-9915f8ae4ec1.png">

`トークンコントラクトアドレス`に、先ほどの実行結果から、`contract address`の値をコピーして入力します。
コントラクトアドレスを入力すると、トークンシンボルとトークンの少数桁数が自動で入力されます。

`カスタムトークンを追加`をクリックすると、以下の画面になります。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/4b2d7edb-a986-46da-2708-540b0bb9e9b9.png">

`トークンをインポート`をクリックすると、以下のような画面が表示されます。
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/c22f0c52-8305-5fb1-67b2-9b7cae8db0a9.png">

トークンが発行されて、自分のウォレットに追加されたことを確認できましたね！
このトークンは、MetaMaskで他のウォレットに送信することができます。

他のウォレットに送信したものを確認する場合は、受け取り側のウォレットで今回と同様に、Mumbaiネットワークの追加とトークンのインポートを行ってください。

## PolygonScanで確認
[PolygonScan](https://mumbai.polygonscan.com/)でコントラクトアドレスを検索し、実際にデプロイされていることを確認できます。
以下は、私が発行したトークンの結果です。
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/eda44402-c882-8a8f-5dba-16156b91540f.png">

# まとめ
Dockerを使って、Mumbai（Polygonのテストネット）に独自トークンをデプロイする方法を解説しました。

今回はERC20をそのまま利用しましたが、本来、スマートコントラクトは様々なカスタマイズを行うのが醍醐味です。特定のNFT保有者だけが受け取れるようにすることもできるので、用途に応じて「MyToken.sol」をカスタマイズしてみましょう。そうすることにより、web3をより深く理解できると思います。

私もカスタマイズしてみようと思うので、いい感じのカスタマイズができたら、また共有しようと思います！:smiley: