HardhatとPinataとNextとVercelでシンプルなNFTミントサイトを作ったので覚書

※自分のための覚書

## フォルダ構成等
Root以下にWebアプリケーションとContractでフォルダを分ける構成
それぞれで作ってしまっても良い。
モノレポを意識した構成にできればなお良いと思っているが今回はそこまでしていない。

client側のartifactsも今手動で持ってきている。
せっかくなのでtypechainちゃんと使ったりしたい気持ちはある。

```
$ tree ./ -L 2
./
├── README.md
├── client // Next x Typescript
│   ├── README.md
│   ├── artifacts // Contract ABI
│   ├── component
│   ├── hooks
│   ├── next.config.js
│   ├── package.json
│   ├── pages
│   ├── public
│   ├── tsconfig.json
│   └── yarn.lock
└── etherium // Contract
    ├── artifacts
    ├── cache
    ├── contracts
    ├── hardhat.config.ts
    ├── package.json
    ├── scripts // Deploy script
    ├── tsconfig.json
    ├── typechain-types
    └── yarn.lock
```

## コントラクト作成
コントラクトはOpenzeppelinのERC721Enumerableを利用
単純なERC721でも良いが、最近のNFTで大体実装されているtotalSupplyの確認などがEnumerableなのでそちらを利用することにした。

通常のERC721はベースとなるURIのみを定義しておくと、あとは連番で動的にURLを生成する。

例えばベースURIに `https://xxx.com/` と指定すると、MintされるごとにそれぞれのNFTのtokenURI（NFTの情報の参照先）が `https://xxx.com/1`、 `https://xxx.com/2` と連番で採番されるようになる。

そのため、metadataのjsonを先に作成して連番で命名して先にサーバーへアップロードしておくことで各NFTがメタデータを参照できるようにする。
メタデータから参照している画像のURIも同様の考え方で採番をしている。


コントラクトの内容自体はチュートリアルで出てくるレベルのもので作っている。
以下の2つのみが今回追加したような内容となる。
- 40個限定となるように `require` を設定
- Mint完了時にClientへ通知が行えるようにeventを設置

コントラクトのコンパイルやDeploy、Verifyには[Hardhat](https://hardhat.org/)を利用
RemixでもTruffleでもなんでも良いのだが、最近の流れ的にはHardhatが主流っぽさを感じている。

コントラクトのVerifyも簡単に行えるのが好き。


```Solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract NFT is ERC721Enumerable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor () ERC721 ("Oh! The Elephant!", "ABEKO") {}

    event Minted(
        address indexed _from, uint256 _tokenId
    );

    function _baseURI() internal view virtual override returns (string memory) {
        return "https://xxxxxx/";
    }

    function mint() public returns (uint256) {
        require(_tokenIds.current() < 40, 'All NFT Minted');
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();

        _safeMint(msg.sender, newItemId);

        emit Minted(msg.sender, newItemId);
        return newItemId;
    }
}

```


## Metadata作成
メタデータの作成は画像データの準備とjsonデータの準備の2通りが必要
パブリックなストレージであればどこでも良いが今回はIPFSを利用

IPFSに対して直接アップロードする方法も読むのは読んだがより手軽な[Pinata](https://www.pinata.cloud/)を利用


メタデータの指定もPinataのGateway使っているのでPinataのサービスが消え去ったら共に消え去るNFTとはなった。（現段階だとどこかしらでWeb2依存は発生するのかなと思ってはいる。完全なWeb3化ももちろん可能ではあるが今はまだ非効率感を感じる）

アップロードする対象の画像は自前で準備してjsonは公開されていたスクリプトを参考にさせてもらった。

https://github.com/neha01/NFT

この人はYoutubeでも解説しているので興味あればどうぞ。

https://www.youtube.com/watch?v=jtbIqUZEfu4



## フロント作成
フロントは普段から利用している技術スタックで作成
- Next（React）
- Typescript
- Chakra UI

Web3系のライブラリとしては以下を利用
- [ethers.js](https://github.com/ethers-io/ethers.js/)

[以前はuseDappを用いたチュートリアルを参考に書いた](https://qiita.com/taaaaho/items/fed4f4fc8371b1d5920b#6-send-react-web3-transactions-via-metamask-with-usedapp)がその前に基本的なライブラリの使い方ぐらいわかってないとと思って今回はethers.jsを採用

web3.jsも考えたがHardhatを選んだ時と同様、ethers.jsの方が主流になっている感があるのでそちらを利用。

個人的にはABIのInterface軽く書くだけでコントラクト叩けるってだけでethers.jsが好き

https://qiita.com/taaaaho/items/b8bae26c9827cf3ea322

https://qiita.com/taaaaho/items/c5fa4e379b206fe8457c


## ホスティング
ボタンひとつで気軽にデプロイできるVercelを利用
必要最低限の設定ができるのはもちろんのこと、ブランチ単位で環境作ってくれたりする神機能もあり。

ドメインの紐付けも簡単。とりあえず良い。
逆にVercel系のClick & Deployほかそんなに使ったことないので井の中の蛙の可能性はある。

というわけでできたのはこんな感じのボタンひとつのシンプルなサイト
![スクリーンショット 2022-05-15 17.34.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245027/c9e78ef6-d4a8-a393-61ba-33e565961702.png)

背景画像はUnsplashのAPIでランダムな画像を持ってきている。
Unsplashの画像はオシャンだから適当にオシャレな感じにしたいときに使ってる。


## 今後
- Client側の制御の改善
    - Max件数とMint件数の表示及び制御
    - UIの改善（アドレス表示等）
- NFTをRevealできるようなコントラクトを作る
    - BaseURIをOwnerが更新可能にする

## 参考

https://morioh.com/p/09e2ba1acd7c

https://eips.ethereum.org/EIPS/eip-721

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

https://zenn.dev/cauchye/articles/ethereum-contract-erc721