![見出し画像](https://assets.st-note.com/production/uploads/images/75397572/rectangle_large_type_2_20a8f89e31a6696e023286d7e08c6d1a.png?width=1200)
第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」を見てみましょう。
![](https://assets.st-note.com/img/1648344983285-qTRtCmos7w.png?width=1200)
2 Reentrantとは?
再入可能という意味のようです。
![](https://assets.st-note.com/img/1648345429548-npfFsyl34K.png?width=1200)
今回はclaim機能にReentrantをしないように「nonReentrant」がついていました。
例えば、ほぼ同時にトークンID1をclaimされたらどうでしょう?
![](https://assets.st-note.com/img/1648345989662-fkqc77FAMv.png?width=1200)
_safeMintの完了に一定の時間がかかることから、もし「nonReentrant」がなければ、同時にclaimすることができてしまいます。
ただ、実際にミントができるのは1人だけです。
そのため、もう一方の人は「ミント済み」というエラーになってしまいます。
一方、nonReentrantがあれば、claimをした瞬間、他の人はtokenId:1をclaimがそもそもできなくなります。
![](https://assets.st-note.com/img/1648346401021-t4BnSESsLp.png?width=1200)
3 具体的にOpenZeppelinを見てみましょう
![](https://assets.st-note.com/img/1648846339564-iSP6ZdyKoK.png?width=1200)
つまり、図にするとこんな感じだと思います。
![](https://assets.st-note.com/img/1648846624822-SjRKGm0FPK.png?width=1200)
4 修飾子の復習をしてみましょう
この勉強会でもよく出てきます、onlyOwnerも見てみましょう。
修飾子なので、実際の処理の部分が「_;」で表されています。
![](https://assets.st-note.com/img/1648846962009-Hob8a6hLkk.png?width=1200)
5 そもそも、なぜReentrantではダメなの?
結論としては、Reentrancy attacksを防ぐために、「nonReentrant」が使われます。
まず、前提として、ステート(状態)は一定に保たれている必要があります。
![](https://assets.st-note.com/img/1648847399629-ThJrqwuM5q.png?width=1200)
![](https://assets.st-note.com/img/1648848017229-yX3ixjQXsv.png?width=1200)
例えば、コントラクトのTotal supplyが100であれば、トークンの総数は100です。
これは下のように、Cさんが引き出しても同じです。
![](https://assets.st-note.com/img/1648374540866-ZSlyXEiRl7.png?width=1200)
しかし、仮に下のような処理がなされている場合、①の瞬間は全供給量が125になっています。
この不整合な状態を狙ってくるのが、「Reentrancy attacks」です。
![](https://assets.st-note.com/img/1648374786740-KReGgMAgjf.png?width=1200)
そのため、関数の処理が完了していない、不整合な状態で処理を行われないために、「nonReentrant」を行い、入って来れないようにしています。
6 Base64について
少し前まで、OpenZeppelinにBase64のライブラリが存在していませんでした。
そのため、Lootなどでは独自のBase64が使われていました。
![](https://assets.st-note.com/img/1648375292999-ubLwc9enKo.png?width=1200)
しかし、 OpenZeppelinでも実装されるようになりました。
Base64を使用する場合には、ぜひこちらもご参照ください。
![](https://assets.st-note.com/img/1648375537579-EcLPFAAV8E.png?width=1200)
7 OnChainMonkeyを参考にジェネレーティブを作ろう!
こちらのプロジェクトを元に、フルオンチェーンのジェネレーティブを学んでみましょう。
https://opensea.io/collection/onchainmonkey
![](https://assets.st-note.com/img/1648376186119-hkh46kKpHh.png?width=1200)
8 フルオンチェーン・ジェネレーティブの作り方(テンプレートの一つ)
OnchainMonkeyから一つのテンプレートを作ってみましょう。
下のようなテンプレートをもとに具体的に見ていきましょう。
①パーツ格納用の構造体を作る
⇨配列の番号である、0,1などの数字を入れる
②確率・色用の配列を用意する
⇨確率用・色用の2つを用意するのがポイント
③パーツを分類する
1)共通パーツ(顔の形など)
2)レア・形共通パーツ(レアの場合、同じ形の帽子など)
3)レア・形変形パーツ(レアの場合、さまざまな形のイヤリングなど)
④SVG合体用の関数を作る
ちなみに、パーツ部分については、次のようになります。
===============
③1)共通パーツ(顔の形など)
(1)色以外のSVGを作り、配列に格納する
(2)合体関数に組み込む
③2)レア・形共通パーツ(レアの場合、同じ形の帽子など)
(1)色以外のSVGを作る
(2)合体関数に組み込む
③3)レア・形変形パーツ(レアの場合、さまざまな形のイヤリングなど)
(1)色以外のSVGを作る
(2)パーツ作成用関数を作る
(3)合体関数に組み込む
===============
では、次の章から具体的に見ていきましょう。
9 見やすいコードにするには?(SVGをわかりやすく)
まずは、tokenURI()を見てみましょう。
とてもコードがスッキリしていますね。
![](https://assets.st-note.com/img/1648376466420-d5FOz26KWK.png?width=1200)
これは上の方で、SVGの部品を変数として格納しているためです。
これにより、コードの可読性を上げています。
![](https://assets.st-note.com/img/1648376475040-0fUldvMVnU.png?width=1200)
とても良いテクニックなのではと思います。
10 構造体について
下のように、「struct」で複数の変数をセットにした構造体を作ることができます。
![](https://assets.st-note.com/img/1648376702725-2pSPciQjjc.png?width=1200)
今回の場合、「Ape」という構造体の中に、bg(背景)、fur(毛並みの色)、eyes、、などの様々な情報を入れています。
![](https://assets.st-note.com/img/1648849762678-KgOjOAj6nd.png?width=1200)
ちなみに、下のようにして値を入れることができます。
![](https://assets.st-note.com/img/1648377074917-06U5Zyem0m.png?width=1200)
11 uint8って何?
今まで、uint256の256などを扱っていなかったので、ここで触れてみたいと思います。
uint8の8はビットを表します。
1ビットは0と1の2通りを表せるため、8ビットは2の8乗である256通りを表すことができます。
つまり、uint256なら、2の256乗の数を表すことができます。
![](https://assets.st-note.com/img/1648378154517-g5zJaGGFtZ.png?width=1200)
そのため、構造体の中で、あらかじめ変数で使う種類がわかっているような場合には、uint8などを用いて、ガス代を節約することができます。
12 重み付けのランダム値を作ってみよう
レア度について、完全なランダムではなく、確率を変えたい場合はどのようにコードを書けば良いでしょう。
![](https://assets.st-note.com/img/1648524323129-oYQN1NjTGt.png?width=1200)
それぞれの要素を見ると「_w」という数字が入った配列があります。
これがポイントです。
![](https://assets.st-note.com/img/1648524151923-sG1rQC74be.png?width=1200)
13 実際に見てみよう
今回はearingを元に見ていきます。
![](https://assets.st-note.com/img/1648525249566-Vg5iQJUVX6.png?width=1200)
usewという関数を使っていますが、2つ目の引数には0~357のどれかの値を渡しています。
![](https://assets.st-note.com/img/1648525288460-L9IIxSQPMh.png?width=1200)
では、下のようにusewという関数を見てみましょう。
「_w」の配列と0~357の擬似ランダムな値が渡されています。
![](https://assets.st-note.com/img/1648525349060-x1RpuxCdzj.png?width=1200)
ランダムな値が0~250(251パターン)の時に0が返されます。
そのため全パターンである358のうち、251の確率で、0が返されます。
つまり、251/358ですね。
ちなみに、358は「_w」の配列を全て足した数になっています。
これで過不足なく抽出できるようになっています。
14 形が同じ・色違い要素を作ってみよう
まずは、形が同じで、色だけが異なるものを抽出しましょう。
これらは形が同じなのでfill以外のSVGは固定されます。
![](https://assets.st-note.com/img/1648526562656-2MIU1EpzTa.png?width=1200)
これらは、下のように、色コードの直前までをそれぞれの要素として、配列に格納することで、便利に利用ができます。
![](https://assets.st-note.com/img/1648526675219-XjAgQSchcP.png?width=1200)
なお、黒目の色などのように、要素の色まで固定の場合は、各要素に入れ込みます。
![](https://assets.st-note.com/img/1648526976493-PV7XpWEF2H.png?width=1200)
その要素を、上のように色と交互に組み合わせています。
15 レア要素を作ってみよう
まずは、どのようなレア度のタイプかを分類してみましょう。
1 形は変わらない
⇨ 帽子・服
2 形も変わる
⇨ 目の形・イヤリング
![](https://assets.st-note.com/img/1648602381348-hqu8qfmdZ7.png?width=1200)
では、それぞれ見ていきましょう。
15ー1 形は変わらないタイプ
まずは、服のように形は変わらないものの実装の仕方を見てみましょう。
下のように、実装されています。
①レアの場合はSVGを入れ、レアでない場合は空白を入れる
②レアか否かに関わらず、abi.encodePacked()でくっつける
![](https://assets.st-note.com/img/1648603082708-fonDnnvZqP.png?width=1200)
具体的に、ape.clothesを見てみましょう。
![](https://assets.st-note.com/img/1648603262296-iQwQCehbNB.png?width=1200)
251 / 1329の確率で0となります。
0の時には「clost」は空白になり、それ以外の時は、それぞれのSVGが入ります。
![](https://assets.st-note.com/img/1648603270115-L6gw20L7eE.png?width=1200)
15ー2 レア度によって形も変わるタイプ
では、形が変わるタイプはどうでしょう。
大まかに、次のような構成になっています。
①特定の関数を作る
②レアの場合はSVGを入れ、レアでない場合は空白を入れる
③レアか否かに関わらず、abi.encodePacked()でくっつける
実は、①特定の関数を作るだけが加わるだけで、あとは同じです。
イヤリングについて見てみましょう。
![](https://assets.st-note.com/img/1648850758706-TWVlIGmbM3.png?width=1200)
あとは、いつもと同様です。
![](https://assets.st-note.com/img/1648604643403-WezPzZs0s4.png?width=1200)
![](https://assets.st-note.com/img/1648604652204-SXyLR5vvdt.png?width=1200)
これで、パーツごとの作り方を具体的に見ることができました。
これを元にすれば、自分自身のオリジナルのジェネレーティブを作ることができるかと思います。
以上です。
サポートをしていただけたらすごく嬉しいです😄 いただけたサポートを励みに、これからもコツコツ頑張っていきます😊