【ERC20】自作トークン備忘録～メインネットで管理できるまで

#Ⅰ. はじめに
ERC20規格の自作トークンを作成した際の手順を残した備忘録です。流れに沿えば、初めてのかたでも手軽にオリジナルトークンを作成できる内容となっています。詳細な説明等は今回省いた点も多いので、適宜他記事やブログご参考いただければと思います。なお、Truffleを利用しての関連記事は比較的多いので、この度は主にhardhatのフレームワーク利用しています。

#Ⅱ. 環境
更新日:2021/07/
OS:Windows10
開発環境:Visual Studio

#Ⅲ. 作業概要
##1. 準備編
###（１）npmインストール
###（２）プロジェクトディレクトリ作成
###（３）OpenZepplinインストール 
###（４）hardhatインストール
###（５）metamask設定とsecretファイル作成
###（６）INFURAアカウント取得
###（７）secretファイル作成
###（８）開発用EHTを無料で入手

##2. ファイル作成編
###（１）contractsファイル作成
###（２）contractsファイル（.sol）コンパイル
###（３）hardhat.config.jsファイル作成
###（４）scriptsファイル作成
###（５）テスト用ファイル作成

##3. テスト＆デプロイ編
###（１）Hardhatネットワークに接続
###（２）hardhat.config.jsにネットワーク情報追加
###（３）テスト
###（４）デプロイ
###（５）metamaskアセット表示

##4. 仕上編
###（１）Solidityソースコードの登録
###（２）etherscanでの所有権表明
###（３）トークン詳細登録
##参考公式サイト
#Ⅳ. 作業詳細
##1. 準備編
###（１）インストール
公式:https://nodejs.org/en/
JavaScript用のパッケージ。推奨版でおそらく良い。

```:（インストール先例）
C:\Users\mydir\AppData\Roaming\npm
```
```:バージョンの確認
$ node --version
v14.17.2
```
###（２）プロジェクトディレクトリ作成

```
// （例）Cドライブの下などに「h_tokens」などの名前でディレクトリを作成
$ mkdir h_tokens
``` 
```
// （例）あらかじめOSのコマンドラインで作成および当該ディレクトリに移動しておく
$ cd h_tokens
```
###（３）OpenZepplinインストール
https://openzeppelin.com/

```
// token/package.jsonが作成される
$ npm init -f

$ npm install openzeppelin-solidity

// OpenZeppelinのContractsライブラリ最新更新分インストール
$ npm install --save-dev @openzeppelin/contracts
```
以下のディレクトリやファイルができる。
token/node_modules/…
token/package-lock.json

###（４）hardhatインストール
https://hardhat.org/tutorial/creating-a-new-hardhat-project.html

```
$ npm install --save-dev hardhat

// ethersプラグイン&chaiインストール
$ npm install --save-dev @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai
// ガスレポーターインストール
$ npm install hardhat-gas-reporter --save-dev
// etherscanインストール
$ npm install --save-dev @nomiclabs/hardhat-etherscan
// ディレクトリとファイルを自動作成
$ npx hardhat
```
![hardat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1753678/027f335d-660b-312e-98f3-77d99db57572.png)
① 矢印キー上下↑↓で「Create a sample project」選択
② インストール先に新規作成したディレクトリ指定「Hardhat project root: » C:\Users\micro\h_tokens」
→'y'入力
以下のディレクトリやファイルができる。
contracts/Greeter.sol
scripts/sample-script.js
test/sample-test.js
.gitignore
hardhat.config.js

###（５）metamaskアカウント取得
まずは検証用のmetamaskアカウントを新規作成する。

```
// ニーモニック（12語のセット・要保存！）発行
$ npx mnemonics
drama film snack motion …
```
metamaskをブラウザより起動し、「シークレット リカバリー フレーズでアカウントを復元する」より上記ニーモニックでアカウント設定。

※ニーモニックは安全かつ機密性をもって保管すること。

###（６）INFURAアカウント取得
https://infura.io/
アカウント取得後「CREATE NEW PROJECT」して詳細を開く。
![infura.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1753678/25fc6349-d7dc-45b5-3554-af9f19180d2c.png)

###（７）secretファイル作成
① 以下の形式のsecrets.jsonファイルを作成。

```json:secrets.json
{
  "mnemonic": "drama film snack motion ...",
  "infuraApiKey": "JPV2..."
}
```
"mnemonic":取得したニーモニックをペースト。
"infuraApiKey":取得したProjectIDをペースト。

② 'hardhat.config.js'と同じディレクトリに置く。

なお、secretファイル安全かつ機密性をもって保管維持すること。
また、のちの工程でもinfura画面は参照するので見られるようにしておくこと。
###（８）開発用EHTを無料で入手
以下のサイトでできる限り送付してもらう。Ropstenネットにはぜひ送ってほしい。
https://gitter.im/kovan-testnet/faucet
https://faucet.rinkeby.io/
https://blog.fukuball.com/dapp/faucet/
https://faucet.ropsten.be/
https://faucet.metamask.io/
https://goerli-faucet.slock.it/
※時間がかかったり、怪しいところもあるので自己責任で。

##2. ファイル作成編
###（１）contractsファイル作成
トークンの仕様を決めるメインファイル。

```solidity:contracts/Greeter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

contract Greeter is ERC20 {
    constructor() ERC20("Greeter", "MMT2") {
        _mint(msg.sender, 222222222222 * 10 ** decimals());
    }
}
```

オリジナルのコントラクトを作成する場合、openzeppelinのウィザードを利用する方法が簡単である。
https://docs.openzeppelin.com/contracts/4.x/wizard
![sample_wiz.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1753678/87fe62c8-dcce-cd38-d03f-10ef8d5c7b00.jpeg)
Name : アセットの名称
Synbol : アセットのシンボル
Premint : 初期発行枚数
Mintable : 管理者の新規発行権限
Burnable : 管理者による発行済アセットの焼却権限
Pausable : 管理者によるシステム停止権限
Snapshots : 管理者による、ある一時点におけるスナップショット取得権限。
Permit : トークン所有者は、ガスを支払うことなく、第三者が自分のアカウントから転送できる。（！要調査不明点あり）
Ownable : すべての管理者が同じ権限を持つ。
Roles : 個々の管理者によって権限の範囲を個別に決めることができる。
Transparent : 調査中
UUPS : 調査中

※注意点
コントラクトの内容を充実させればさせるだけ、mainnetにデプロイする際のガス代が大幅にコスト高になる。（1ETHくらいになることZARA）
###（２）contractsファイル（.sol）コンパイル

```
$ npx hardhat compile
```
成功すると以下のように表示される。
![comp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1753678/5ae6cdf2-b487-bbc1-8f0d-c7dc1dbcb7a8.png)
※コントラクトファイルの内容を少しでも変更することあれば、その都度上記作業が必要。
###（３）hardhat.config.jsファイル作成
https://docs.openzeppelin.com/learn/deploying-and-interacting#deploying-a-smart-contract

```js:hardhat.config.js
require("@nomiclabs/hardhat-waffle");
require('@nomiclabs/hardhat-ethers');
require("@nomiclabs/hardhat-etherscan");
require('@openzeppelin/hardhat-upgrades');
require("hardhat-gas-reporter");

task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});
/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.4",
};
```
###（４）scriptsファイル作成
コントラクトをネットワークにデプロイするために必要。
https://docs.openzeppelin.com/learn/deploying-and-interacting#deploying-a-smart-contract

```js:scripts/sample-script.js
const hre = require("hardhat");

async function main() {

  const Greeter = await hre.ethers.getContractFactory("Greeter");
  const greeter = await Greeter.deploy(); // 引数は入れないほうがよさそう
  await greeter.deployed();
  // コントラクトアドレス
  console.log("Greeter deployed contracts address to:", greeter.address);
　// TXアドレス
  console.log("Greeter deployed hash to:", greeter.deployTransaction.hash);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });
```
###（５）テスト用ファイル作成
https://hardhat.org/tutorial/testing-contracts.html

```js:test/sample-test.js
const { expect } = require("chai");

describe("Greeter contract", function () {
  it("Deployment should assign the total supply of tokens to the owner", async function () {
    const [owner] = await ethers.getSigners();

    const greeter = await ethers.getContractFactory("Greeter");

    const hardhatToken = await greeter.deploy();

    const ownerBalance = await hardhatToken.balanceOf(owner.address);
    expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
  });
});
```

##3. テスト＆デプロイ編
###（１）Hardhatネットワークに接続
```
// ウォレットまたはDappをHardhatネットワークに接続する
$ npx hardhat node
```
接続に成功すると以下形式でずらっと表示される。

```
Accounts
========
Account #n: 0xf39fd6e51aad88f6f………fffb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff94………
・
・
・
```
作業中継続して起動させておく必要あり。コマンドをもう一つ別に開き作業を続ける。(この時再度作業ディレクトリにcd…で変更)

###（２）Ropsten,Rinkeby,Mainnetネットワークに接続
注意！
本番環境であるMainnetにデプロイする前にRopsten,Rinkebyでそれぞれテストをする。（便宜上、この記事ではRopstenから説明する。）
それそれのネットワーク解説はこちら : https://docs.openzeppelin.com/learn/connecting-to-public-test-networks#testnet-list

「2. ファイル作成編-（５）hardhat.config.jsファイル作成」に以下の内容を書き足す。
https://docs.openzeppelin.com/learn/connecting-to-public-test-networks#configuring-the-network
ネットワーク接続先によってネットワーク名および、（infuraキャプチャ画面の緑のアンダーラインURL）の部分を切り替える。

```js:hardhat.config.js(ropsten)の場合
+ const { infuraApiKey, mnemonic } = require('./secrets.json');
...
  module.exports = {
+    networks: {
+     ropsten: {
+       url: `https://（infuraキャプチャ画面の緑のアンダーラインURL）/${infuraApiKey}`,
+       accounts: { mnemonic: mnemonic },
+     },
+   },
...
};
```
###（３）テスト編
ファイルを個別指定しないと、testディレクトリにあるファイルがすべて精査の対象となること注意。

```
$ npx hardhat test
// 個別指定する場合は $ npx hardhat test test/sample-test.js
```
成功すると、ガス代情報と合わせ以下のように表示される。
![testok.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1753678/3e5d0b1c-e41c-6e62-6e5b-8b23e57d0c65.png)
参考
https://hardhat.org/plugins/hardhat-gas-reporter.html
https://dapps.gamewith.jp/?p=90

###（４）デプロイ
① --networkのパラメータを検証対象のネットワーク名に上書き。（例 : Ropsten）
② デプロイ

```
// デプロイ
$ npx hardhat run scripts/sample-script.js --network ropsten
Greeter deployed contracts address to: 0xEA3B81eDc45726CE6A3314bd3…cA200A……
Greeter deployed hash to: 0xb5cd97870c97934d0765…3f59f4aa6649fbbb6f0…f7fa94c…64dee9………
```
③ トランザクションが通っているかを確認。
ropsten : https://ropsten.etherscan.io/
デプロイした時の「Greeter deployed hash to: 」右のTXアドレスを検索窓に貼り付けて検索。
![su.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1753678/0ed2abb5-4bb9-7a79-4936-e190e2753144.png)
成功していることを確認。

###（５）metamaskアセット表示
① 「トークンの追加」を押下。
![mts.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1753678/fc3a460b-c8dd-d38c-8c7d-ca3a846c9931.png)
② デプロイした時の「Greeter deployed hash to: 」右のTXアドレスを追加して「次へ」押下。
![mts2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1753678/15e592eb-c74d-cf2f-2e3c-d2be45391908.png)
③ 最終的にアセットのリストに追加される。

他、Rinkebyについても同じテスト、動作確認、作業を実施しておく。

```js:hardhat.config.js(rinkebyの場合)
+ const { infuraApiKey, mnemonic } = require('./secrets.json');
...
  module.exports = {
+    networks: {
+     rinkeby: {
+       url: `https://infuraキャプチャ画面の緑のアンダーラインURL/${infuraApiKey}`,
+       accounts: { mnemonic: mnemonic },
+     },
+   },
...
};
```
rinkeby : https://rinkeby.etherscan.io/


本番環境ネットワーク（mainnet）も同様につつがなく作業。

```js:hardhat.config.js(mainnet)
+ const { infuraApiKey, mnemonic } = require('./secrets.json');
...
  module.exports = {
+    networks: {
+     mainnet: {
+       url: `https://infuraキャプチャ画面の緑のアンダーラインURL/${infuraApiKey}`,
+       accounts: { mnemonic: mnemonic },
+     },
+   },
...
 };
```
mainnet : https://etherscan.io/

##4. 仕上編
###（１）Solidityソースコードの登録
コントラクトをメインネットにデプロイ後、ソースコードの確認をする。（SolidityコードをEtherscanやEtherchainなどのサードパーティに送信）

参考
https://docs.openzeppelin.com/learn/preparing-for-mainnet
https://hardhat.org/plugins/nomiclabs-hardhat-etherscan.html

① https://etherscan.io/ アカウント取得
② login後、「API-KEYs」にて新規APIキー発行。
③ 以下のようなファイルを作成。

```json:etherscanapikey.json
{
  "etherscanApiKey": "（APIキー）…YQFH…VVPY3…XMA4K…FWR32…"
}
```
'hardhat.config.js'と同じディレクトリに置く。
④ 'hardhat.config.js' に以下の記述を足す。

```js:hardhat.config.js
...
const { etherscanApiKey } = require('./etherscanapikey.json');
...
module.exports = {
  networks: {
    mainnet: { ... }
  },
  etherscan: {
    apiKey: etherscanApiKey
  }
};
```
⑤ verifyする

```
$ npx hardhat verify --network mainnet --contract contracts/Greeter.sol:Greeter （コントラクトアドレス付加）0x0c…88317e2d7304887…626dcc…c3c34… （引数）1
```
もしも引数がなければ（不要であれば）以下のような赤字エラーが表示される。

```
Error in plugin @nomiclabs/hardhat-etherscan: The constructor for contracts/Greeter.sol:Greeter has 0 parameters
but 1 arguments were provided instead.
```
その時は引数は削除する。成功すると以下のような表示となる。

```
Compiling 1 file with 0.8.4
Successfully submitted source code for contract
contracts/Greeter.sol:Greeter at 0x0c…88317e2d7304887…626dcc…c3c34…
for verification on Etherscan. Waiting for verification result...

Successfully verified contract Greeter on Etherscan.
https://etherscan.io/address/0x0c…88317e2d7304887…626dcc…c3c34…#code
```
なお、ブラウザで以下URLにアクセスすると、etherscan画面でも確認できる。
https://etherscan.io/address/0x0c…88317e2d7304887…626dcc…c3c34…#code

###（２）etherscanでの所有権表明
ログイン後、以下画面に遷移
https://etherscan.io/myverify_address
「Add」ボタンでコントラクトアドレスを入力し、画面の指示に従いながら先に進む。
参考
https://info.etherscan.com/how-to-verify-address-ownership/

###（３）トークン詳細登録
トークン詳細画面に戻り
https://etherscan.io/token/0x0c…88317e2d7304887…626dcc…c3c34…

Profile Summary の [Edit] リンクからプロフィールをなるべく詳細に登録して、etherscan 運営からのお便りを待つ。

一旦ここで今回は終了！

###参考公式サイト
https://ethereum.org/ja/developers/
https://www.trufflesuite.com/
https://openzeppelin.com/
https://vittominacori.github.io/erc20-generator/