【NFT】独自コントラクトでmintしたNFTをOpenSeaのテストネット（Polygonチェーン）で確認するまでの覚書


## まえがき
昨年の12月はじめ頃からOpenSeaでNFTの販売を始め、有り難いことに、現在どのコレクションも沢山の方に見ていただけるようになりました。

今年の1月末頃、OpenSeaが
` 「共有コントラクトでの出品は5コレクション迄、且つ1コレクションにつき50itemまで」 ` 
という声明を公式に発表して以来（結局これは一夜で撤回されることになりましたが）、独自コントラクトへ移行しようという流れが多く見られるようになりましたよね。

私自身は、現状もOpenSeaの共有コントラクトを使用して出品を続けており、今後も、前述の「OpenSea事変」のようなことが起きない限り、独自コントラクトで出品する予定は今の所ありません。
が、独自コントラクト自体には以前から少し興味がありましたので、今回、[@razokulover](https://twitter.com/razokulover)さんの記事（　[NFT完全に理解した！！になるために独自コントラクトでNFTを発行してみる方法の解説](https://zenn.dev/razokulover/articles/7db2340f14c2cd)　）を参考に、Polygonチェーン（Mumbai使用）でmintしたNFTをOpenSeaのテストネットで確認するところまでやってみました。

正直、手法はまるまる上記記事の亜流ですし、チョコファクトリー等のサービスが登場した今、公開するほどのものでもないかと迷いましたが、独自コントラクト且つPolygonチェーンでmintするまでの一連の流れが一つの記事にまとまったものは、私の探す限りでは（2022年2月3日時点）見当たらず、躓いた点も多々あったので、一個人の覚書としてここに残しておきたい所存です。

認識等、誤っている箇所がありましたら、ご指摘いただけますと大変幸いです！
## 前準備編
#### 【環境・ツール編】


 ●Node.js
 バージョン 16.13.2
Windowsの場合は [nvm-windows](https://github.com/coreybutler/nvm-windows/releases) から最新のLTS（安定版）をインストールしてください。

 ●Visual Studio Code
正味メモ帳でも何でもいいです。

●サーバ（URLでjsonにアクセスするための"置き場"）
今回のテストではIPFS化はせずに、かりそめにconohaのレンタルサーバを使用しましたが、AWSのS3を使用している方が多いのかな？という印象です。
ただ、conohaのファイルマネージャにglbをアップロードしても（おそらく）バイナリデータとして読んでくれなかったので、今回はpngでテストした結果を記します。

## ローカル編

#### 【環境構築】
##### HardhatとOpenZeppelinのインストール
HarthatとはEthereumのスマートコントラクト開発の補助ツール、OpenZeppelinとはスマートコントラクト用のライブラリです。
まずはこの2つが必要なのでインストールします。

どこでもいいので今回のテスト用フォルダを作成し、そのディレクトリに移動して下記コマンドを実行します。

```
npm init --yes
npm install --save-dev hardhat
npm install @openzeppelin/contracts web3
npx hardhat
```
ここで、モジュールが足りないエラーが出た場合は、`npm install xxxx（足りないと言われたモジュール名）`でインストールしてください。
うまく実行できると、サンプルプロジェクト作成を選ぶコマンドが出ます。
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/f2b0d3b2-9b8c-2a77-d3c7-9f2a3735f058.png)

一番上の`Create a basic sample project`を選び、サンプルプロジェクトを作成するディレクトリを指定します。
git管理するつもりなら、gitignoreは入れておけば良いと思います。
以上を選択し終えると、指定したディレクトリにプロジェクトが作成されます。
![キャプチャ1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/ddd36f7f-431a-2fb5-3529-65f8a4e07394.png)

無事にサンプルプロジェクトが作られたら、以下コマンドでテストが通るかを確認します。

```
npx hardhat test
```

うまく通れば、 `1 passing`と表示されるはずです。
（もし何かモジュールが足りないようなエラーが出たら、それもインストールして、通るまでテストを行います）




##### コントラクトの作成

サンプルプロジェクトにあるcontractsフォルダ内のGreeter.solを削除して、代わりに自分のコントラクト（この例ではnfttest.solとしました）を作成します。nfttest.solの中身は以下のようにします。

```javascript:nfttest.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.4;

import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";

contract nfttest is ERC721PresetMinterPauserAutoId {
    constructor()
        ERC721PresetMinterPauserAutoId("任意のコントラクト名", "コントラクトの省略名", "https://～(NFTのベースフォルダ)")
    {}
}
```

※ここで指定したベースフォルダに、発行したコントラクトID（0から順に発行されます）が付帯したものが各NFTのURLとなります。（例：https://(ベースフォルダ)/0, https://(ベースフォルダ)/1, ・・・）


#### 【デプロイ】

##### デプロイ用スクリプトの作成

次にデプロイ用のスクリプトを作成します。
サンプルプロジェクトのscriptsフォルダにあるsample-script.jsを削除して、代わりにdeploy.jsを作成します。中身は以下のようにします。

```javascript:deploy.js
const hre = require("hardhat");

async function main() {
  const NFT = await hre.ethers.getContractFactory("nfttest");
  const nft = await NFT.deploy();

  await nft.deployed();

  console.log("NFT deployed to:", nft.address);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

```

##### デプロイの実行
スクリプトができたら、いよいよデプロイします。まずはローカル環境でテストしましょう。

以下のコマンドを実行することで、10000ETHを持ったアカウントが20個ほどつくられて架空のお金持ちになれます！ローカル環境にデプロイする時にはこのアカウント（#0）を使用します。

※このコマンドは、実行後に制御が戻ってこないので、別ウィンドウでもうひとつコマンドプロンプトを立ち上げて実行し、開きっぱなしにしておいてください。

```
npx hardhat node
```


無事にお金持ちアカウントがローカルにたくさん作られたらデプロイ準備完了です。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/4381e486-24df-3940-a339-e898797db6d0.png)



これまで作業していたコマンドプロンプトで、先程作成したデプロイ用のスクリプトを実行します。

```
npx hardhat run --network localhost scripts/deploy.js
```

無事にデプロイが完了すると、以下のメッセージが表示されます。

```
NFT deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
```
ここに表示された数字がコントラクトアドレスになるので、メモしておきましょう。


#### 【NFTの発行（mint）】

##### mint用スクリプトの作成

さて、次はNFTの発行（mint）を行うためのスクリプトを作成します。以下のスクリプトをscripts以下にmint.jsとして作成します。

```javascript:mint.js
const Web3 = require("web3");

// ADDRESS, KEY and URL are examples.
const CONTRACT_ADDRESS = "メモしておいたアドレス";
const PUBLIC_KEY = "架空の金持ちアカウント#0のPublic Key";
const PRIVATE_KEY =
  "架空の金持ちアカウント#0のPrivate Key";
const PROVIDER_URL = "http://localhost:8545";

async function mintNFT() {
  const web3 = new Web3(PROVIDER_URL);
  const contract = require("../artifacts/contracts/nfttest.sol/nfttest.json");
  const nftContract = new web3.eth.Contract(contract.abi, CONTRACT_ADDRESS);
  const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest");
  const tx = {
    from: PUBLIC_KEY,
    to: CONTRACT_ADDRESS,
    nonce: nonce,
    gas: 500000,
    data: nftContract.methods.mint(PUBLIC_KEY).encodeABI(),
  };

  const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
  signPromise
    .then((signedTx) => {
      const tx = signedTx.rawTransaction;
      if (tx !== undefined) {
        web3.eth.sendSignedTransaction(tx, function (err, hash) {
          if (!err) {
            console.log("The hash of your transaction is: ", hash);
          } else {
            console.log(
              "Something went wrong when submitting your transaction:",
              err
            );
          }
        });
      }
    })
    .catch((err) => {
      console.log("Promise failed:", err);
    });
}

mintNFT();
```

##### mintの実行

ローカル環境でmint.jsを実行します。

```
npx hardhat run --network localhost scripts/mint.js
```

##### 発行されたNFTの確認

mintされた個数を表示するスクリプトを作成・実行して、ただしく発行できたかを確認しておきましょう。

```javascript:view.js
const Web3 = require("web3");

// ADDRESS, KEY and URL are examples.
const CONTRACT_ADDRESS = "メモしておいたアドレス";
const PUBLIC_KEY = "架空の金持ちアカウント#0のPublic Key";
const PROVIDER_URL = "http://localhost:8545";

async function viewNFT() {
  const web3 = new Web3(PROVIDER_URL);
  const contract = require("../artifacts/contracts/nfttest.sol/nfttest.json");
  const nftContract = new web3.eth.Contract(contract.abi, CONTRACT_ADDRESS);
  nftContract.methods.balanceOf(PUBLIC_KEY).call().then(console.log);
}

viewNFT();
```

以下コマンドで実行します。

```
npx hardhat run --network localhost script/view.js
```
正しく実行できれば、ここまでにmintしたNFTの個数が表示されるはずです。


## テストネット編

ローカル環境で正しく動作することが確認できたら、実際のネットワーク上にコントラクトをデプロイしてNFTを発行していきます。

本番環境の前に、テスト用のネットワークがあるので、そちらで進めていきます。テスト用のネットワークはいくつかありますが、今回は、PolygonチェーンのテストネットMumbaiを使用します。OpenSeaのテストネットにも対応しているので、普段OpenSeaを使用している人は分かりやすくてよいでしょう。

大まかな流れとしては、
1. ウォレットにpolygonのテストネット（Mumbai）を追加し、テスト用のMaticを取得
2. チェーンにアクセスするためのURLをAlchemyというサービスを使って取得
3. テストネット上にデプロイ＆mint
4. OpenSeaのテストネットで確認
のように行っていきます。

#### 【テストネット用の環境作り】
##### ウォレットにPolygonのテストネット（Mumbai）を追加
MetaMaskにログインして、「ネットワークの追加」からPolygonのテストネット（Mumbai）を追加するか、
https://mumbai.polygonscan.com/ の右下にあるAdd Mumbai Networkボタンから追加してください。

※2022年2月11日現在の情報
![6キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/4bf0423f-6de5-0df1-fa85-dead9f7d5f4f.png)


| RPC Url | チェーンID | ブロックエクスプローラーのURL | 通貨記号 |
|:-----------|------------:|:------------:|:------------:|
| https://matic-mumbai.chainstacklabs.com     | 80001        | https://mumbai.polygonscan.com        |　Matic         |


##### テスト用の0.5Maticを取得する

https://faucet.polygon.technology/ にアクセスし、

- Mumbai
- MATIC Token

を選択して、ウォレットアドレスを入力し、Submitしましょう。

![5キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/5db7f72c-f26a-b117-f24d-0584f1e8f82b.png)

MetaMask上で、0.5Matic増えたことを確認できればOKです。
![7キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/70b9c619-ff73-4027-9d22-f35cd23b9c65.png)


##### Alchemyでアカウントの作成

実際にチェーンに対してデプロイを行うためには、そのチェーンに参加しているノードにアクセスすることが必要となりますが、Alchemyというサービスを使うことで、アクセス先のURLを得ることができます。

https://www.alchemy.com/ こちらから新規登録し、Create Appsで適当にプロジェクトを作成します。作成したプロジェクトページに飛ぶと、[VIEW KEY]ボタンからHTTP URLを取得できるので控えておきます。




#### 【デプロイ】

##### デプロイ設定にテストネットのURLを追加

hardhat.config.js を開いて、テストネット用のURLを追加します。

```javascript
require("@nomiclabs/hardhat-waffle");

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
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);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.4",
  networks: {
    mumbai: {
      url: "Alchemyで得たmumbaiのURL",
      accounts: ["自分のウォレットの秘密鍵"],
    },
  },
};

```

##### コントラクトをデプロイ

以下のコマンドを実行してデプロイします。ローカル環境でやったときと同じコマンドで、ネットワークの指し先をlocalhostからmumbaiに変更しています。

```
npx hardhat run --network mumbai scripts/deploy.js
```

成功すると、コントラクトアドレス（0xで始まる文字列）が表示されるので、それをメモしておきます。
ちなみに、この段階でガス代分のMaticがわずかに減っていることを確認できます。

#### 【Mint】

##### 実際のファイルを置く

今回かりそめに用意したconohaのレンタルサーバに、下記のpng画像（NFT化してmintするもの）とjsonファイルをアップロードしておきます。

![test-mint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/ea0a2695-4bfe-490c-6114-431ae80f448b.png)

```
{
    "name": "This is usako's first test NFT.",
    "external_url": "https://~(用意したサーバのアドレス)~/0",
    "image": "https://~(用意したサーバのアドレス)~/test-mint.png",
    "description": "This is test."
}

```

##### いよいよmint

次に、mint.jsの先頭部分を下記のように書き換えます。

```javascript:mint.js
const CONTRACT_ADDRESS = "deploy成功時に表示された文字列";
const PUBLIC_KEY = "テスト用ウォレットのPublic Key";
const PRIVATE_KEY =
  "テスト用ウォレットのPrivate Key";
const PROVIDER_URL = "Alchemyで取得したURL";

```

書き換えたら、以下コマンドを実行します。

```
npx hardhat --network mumbai script/mint.js
```

成功したらNFT発行完了です。ここでもガス代としてMaticがわずかに減っていることが確認できます。


#### 【OpenSeaのテストネットで確認】

https://testnets.opensea.io/
OpenSeaのテストネットで、自分のコレクションページを見てみましょう。
コレクションが作成されています。

![9キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/ca0417af-4f2a-a72f-8f70-9becbef1fb36.png)

今回テスト用に用意したpng画像も作品としてmintされていることが確認できます！
![8キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/1c49f54d-27cd-0362-a99f-212935b90002.png)


![10キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/203f87dc-4206-cddc-6028-74e1317618a8.png)

本番環境にも同様の流れで行うことができます！

## おまけ
私が現在OpeanSeaにて運営しているコレクションです。
是非ご覧いただけますと幸いです★

### 【イラスト作品】

#### ●[だきしめうさこちゃん](https://opensea.io/collection/usako-chan-holding)
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/24927369-51b8-c0fc-49a7-d2a4be1e61af.png">
かわいいうさぎのうさこちゃんが、「まるくて可愛いもの」を抱きしめます！

#### ●[ある日のうさこちゃん](https://opensea.io/collection/usako-chan-life)
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/fe869446-f8b2-b1e4-6182-50f4f7ece755.png">
うさこちゃんたちのゆるかな日常風景作品です！

#### ●[SEIFUKU kawaii](https://opensea.io/collection/seifuku-kawaii)
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/342bf04b-e2e7-d4a6-8cbb-6b052b95e4dd.png">
「日本の可愛い制服」をコンセプトに、47都道府県イメージの制服ガールズを描いています！

#### ●[ちびSEIFUKU kawaii](https://opensea.io/collection/seifuku-chibi)
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/814965db-158b-f001-d358-c5aacf493a00.png">
「SEIFUKU kawaii」の一次流通時のオーナー様へお送りしている特典イラストです！ 

### 【3D作品】
#### ● [うさこちゃんドールハウス](https://opensea.io/collection/3d-dollhouse)
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/6f369e5a-5889-59f8-fbda-04a280be2e2f.png">
正方形の空間の隅々まで、うさこちゃんの「可愛い」をぎゅっと詰め込んだ3Dドールハウスコレクションです！

#### ● [うさこちゃんアクセサリー](https://opensea.io/collection/3d-dollhouse)
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/5b30b898-8ae3-ffdb-50ca-4d6f9d14e0a5.png">
うさこちゃんの「可愛い」が詰まった小物系3Dコレクションです！

#### ● [ペラペラ制服ちゃん](https://opensea.io/collection/3d-dollhouse)
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/0ad4f720-2685-4cf6-fe98-126549195500.gif">



ちび制服ちゃんが、ペラペラな3Dで登場！イラストでは見ることのできない後ろ姿も必見です！まばたきもしますよ★


### 【ゲーム作品】
#### ● [Usako-chan's Game Collection](https://opensea.io/collection/usako-chan-s-game-collection)
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247873/9925a93f-1c62-a923-f07f-5c354876a387.gif">
ゲームの概要が分かるプレイ動画をNFT化して、Unlockable Content内に実際にゲームがプレイできるURL（WebGL）を置いています！今後展開していきたいゲームのアイデアはオリジナルもファンアートも含め沢山…！何せ作る時間が足りないけれど、ゲームコレクションもどんどん力を入れていきます！ 

## 参照元一覧

https://docs.opensea.io/docs/metadata-standards

https://docs.polygon.technology/docs/develop/network-details/network/

https://zenn.dev/razokulover/articles/7db2340f14c2cd