※自分のための覚書
フォルダ構成等
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を利用
RemixでもTruffleでもなんでも良いのだが、最近の流れ的にはHardhatが主流っぽさを感じている。
コントラクトのVerifyも簡単に行えるのが好き。
// 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を利用
メタデータの指定もPinataのGateway使っているのでPinataのサービスが消え去ったら共に消え去るNFTとはなった。(現段階だとどこかしらでWeb2依存は発生するのかなと思ってはいる。完全なWeb3化ももちろん可能ではあるが今はまだ非効率感を感じる)
アップロードする対象の画像は自前で準備してjsonは公開されていたスクリプトを参考にさせてもらった。
この人はYoutubeでも解説しているので興味あればどうぞ。
フロント作成
フロントは普段から利用している技術スタックで作成
- Next(React)
- Typescript
- Chakra UI
Web3系のライブラリとしては以下を利用
以前はuseDappを用いたチュートリアルを参考に書いたがその前に基本的なライブラリの使い方ぐらいわかってないとと思って今回はethers.jsを採用
web3.jsも考えたがHardhatを選んだ時と同様、ethers.jsの方が主流になっている感があるのでそちらを利用。
個人的にはABIのInterface軽く書くだけでコントラクト叩けるってだけでethers.jsが好き
ホスティング
ボタンひとつで気軽にデプロイできるVercelを利用
必要最低限の設定ができるのはもちろんのこと、ブランチ単位で環境作ってくれたりする神機能もあり。
ドメインの紐付けも簡単。とりあえず良い。
逆にVercel系のClick & Deployほかそんなに使ったことないので井の中の蛙の可能性はある。
というわけでできたのはこんな感じのボタンひとつのシンプルなサイト
背景画像はUnsplashのAPIでランダムな画像を持ってきている。
Unsplashの画像はオシャンだから適当にオシャレな感じにしたいときに使ってる。
今後
- Client側の制御の改善
- Max件数とMint件数の表示及び制御
- UIの改善(アドレス表示等)
- NFTをRevealできるようなコントラクトを作る
- BaseURIをOwnerが更新可能にする
参考