第6回 solidity学習会 講義ノート(4/2 AM8:00~)
こんにちは、CryptoGamesの高橋です。
クリスペの会社です。
また、CryptoMaidsのアンバサダーも務めさせていただいております。
今回は、4/2 AM8:00から次の場所で勉強会を行いますので、その講義ノートの公開です。
場所 meet.google.com/cas-bnjw-rpg
予習や復習などに役立てていただければ幸いです。
では、やっていきましょう。
1 はじめに
前回に引き続き、RemixにコピペしたLootのコードを元に進めていきます。
こちらがLootのコードです。
https://etherscan.io/address/0xff9c1b15b16263c61d017ee9f65c50e4ae0113d7
Remixはこちらです。
まずはじめに、こちらの「nonReentrant」を見てみましょう。
2 Reentrantとは?
再入可能という意味のようです。
今回はclaim機能にReentrantをしないように「nonReentrant」がついていました。
例えば、ほぼ同時にトークンID1をclaimされたらどうでしょう?
_safeMintの完了に一定の時間がかかることから、もし「nonReentrant」がなければ、同時にclaimすることができてしまいます。
ただ、実際にミントができるのは1人だけです。
そのため、もう一方の人は「ミント済み」というエラーになってしまいます。
一方、nonReentrantがあれば、claimをした瞬間、他の人はtokenId:1をclaimがそもそもできなくなります。
3 具体的にOpenZeppelinを見てみましょう
つまり、図にするとこんな感じだと思います。
4 修飾子の復習をしてみましょう
この勉強会でもよく出てきます、onlyOwnerも見てみましょう。
修飾子なので、実際の処理の部分が「_;」で表されています。
5 そもそも、なぜReentrantではダメなの?
結論としては、Reentrancy attacksを防ぐために、「nonReentrant」が使われます。
まず、前提として、ステート(状態)は一定に保たれている必要があります。
例えば、コントラクトのTotal supplyが100であれば、トークンの総数は100です。
これは下のように、Cさんが引き出しても同じです。
しかし、仮に下のような処理がなされている場合、①の瞬間は全供給量が125になっています。
この不整合な状態を狙ってくるのが、「Reentrancy attacks」です。
そのため、関数の処理が完了していない、不整合な状態で処理を行われないために、「nonReentrant」を行い、入って来れないようにしています。
6 Base64について
少し前まで、OpenZeppelinにBase64のライブラリが存在していませんでした。
そのため、Lootなどでは独自のBase64が使われていました。
しかし、 OpenZeppelinでも実装されるようになりました。
Base64を使用する場合には、ぜひこちらもご参照ください。
7 OnChainMonkeyを参考にジェネレーティブを作ろう!
こちらのプロジェクトを元に、フルオンチェーンのジェネレーティブを学んでみましょう。
https://opensea.io/collection/onchainmonkey
8 フルオンチェーン・ジェネレーティブの作り方(テンプレートの一つ)
OnchainMonkeyから一つのテンプレートを作ってみましょう。
下のようなテンプレートをもとに具体的に見ていきましょう。
ちなみに、パーツ部分については、次のようになります。
===============
③1)共通パーツ(顔の形など)
(1)色以外のSVGを作り、配列に格納する
(2)合体関数に組み込む
③2)レア・形共通パーツ(レアの場合、同じ形の帽子など)
(1)色以外のSVGを作る
(2)合体関数に組み込む
③3)レア・形変形パーツ(レアの場合、さまざまな形のイヤリングなど)
(1)色以外のSVGを作る
(2)パーツ作成用関数を作る
(3)合体関数に組み込む
===============
では、次の章から具体的に見ていきましょう。
9 見やすいコードにするには?(SVGをわかりやすく)
まずは、tokenURI()を見てみましょう。
とてもコードがスッキリしていますね。
これは上の方で、SVGの部品を変数として格納しているためです。
これにより、コードの可読性を上げています。
とても良いテクニックなのではと思います。
10 構造体について
下のように、「struct」で複数の変数をセットにした構造体を作ることができます。
今回の場合、「Ape」という構造体の中に、bg(背景)、fur(毛並みの色)、eyes、、などの様々な情報を入れています。
ちなみに、下のようにして値を入れることができます。
11 uint8って何?
今まで、uint256の256などを扱っていなかったので、ここで触れてみたいと思います。
uint8の8はビットを表します。
1ビットは0と1の2通りを表せるため、8ビットは2の8乗である256通りを表すことができます。
つまり、uint256なら、2の256乗の数を表すことができます。
そのため、構造体の中で、あらかじめ変数で使う種類がわかっているような場合には、uint8などを用いて、ガス代を節約することができます。
12 重み付けのランダム値を作ってみよう
レア度について、完全なランダムではなく、確率を変えたい場合はどのようにコードを書けば良いでしょう。
それぞれの要素を見ると「_w」という数字が入った配列があります。
これがポイントです。
13 実際に見てみよう
今回はearingを元に見ていきます。
usewという関数を使っていますが、2つ目の引数には0~357のどれかの値を渡しています。
では、下のようにusewという関数を見てみましょう。
「_w」の配列と0~357の擬似ランダムな値が渡されています。
ランダムな値が0~250(251パターン)の時に0が返されます。
そのため全パターンである358のうち、251の確率で、0が返されます。
つまり、251/358ですね。
ちなみに、358は「_w」の配列を全て足した数になっています。
これで過不足なく抽出できるようになっています。
14 形が同じ・色違い要素を作ってみよう
まずは、形が同じで、色だけが異なるものを抽出しましょう。
これらは形が同じなのでfill以外のSVGは固定されます。
これらは、下のように、色コードの直前までをそれぞれの要素として、配列に格納することで、便利に利用ができます。
なお、黒目の色などのように、要素の色まで固定の場合は、各要素に入れ込みます。
その要素を、上のように色と交互に組み合わせています。
15 レア要素を作ってみよう
まずは、どのようなレア度のタイプかを分類してみましょう。
では、それぞれ見ていきましょう。
15ー1 形は変わらないタイプ
まずは、服のように形は変わらないものの実装の仕方を見てみましょう。
下のように、実装されています。
具体的に、ape.clothesを見てみましょう。
251 / 1329の確率で0となります。
0の時には「clost」は空白になり、それ以外の時は、それぞれのSVGが入ります。
15ー2 レア度によって形も変わるタイプ
では、形が変わるタイプはどうでしょう。
大まかに、次のような構成になっています。
実は、①特定の関数を作るだけが加わるだけで、あとは同じです。
イヤリングについて見てみましょう。
あとは、いつもと同様です。
これで、パーツごとの作り方を具体的に見ることができました。
これを元にすれば、自分自身のオリジナルのジェネレーティブを作ることができるかと思います。
以上です。