【完全保存版】Diamond Proxy Patternでコントラクトを作ってみよう!
0 はじめる前に
今回は、Goerliというテストトークンを使います。
Mumbaiでもできるはずですが、私がやった時にはうまく行かなかったためです。
なお、「Goerli」は「0.001ETH」以上入っているウォレットからでなければ、取得できません。
始める前に、ご留意ください。
1 概要
ERC2535の「Diamonds, Multi-Facet Proxy」を一緒学んでみましょう。
これは、その名の通り、「Diamonod」というコントラクトと、「Facet」というたくさんのコントラクトから構成されます。
これにより、コントラクトの容量を気にすることなく、「Facet」でたくさんの機能を追加できます。
下のように、メインは「Diamond」コントラクトです。
「Facet」の機能を呼び出したい時には、「Diamond」から呼び出します。
そして、そのデータは「Facet」にではなく、「Diamonod」に存在します。
ここは大きなポイントです。
そのため、データは「Diamond」、ロジックは「Facet」というように分離することができます。
2 Diamondコントラクトを作ってみよう
1 コントラクトの作成
では、実際にやってみましょう。
この記事は、こちらのYouTubeを元にしました。
まずは、Remixを立ち上げます。
そして、こちらから、「Clone」を選択します。
下のGithubのURLを貼り付けて、「OK」
https://github.com/ytakahashi2020/diamond_proxy.git
なお、こちらの「Github」です。
では、「contracts」を見てみましょう。
まずは、「FuncDiamond.sol」を開きます。
こちらが「Diamond」コントラクトです。
次に、下のように、コンパイルを行います。
なお、このような警告が出ています。
内容としましては、「payable」の「fallback」関数があるのに「receive」関数がないため警告が出ています。
この辺りについては、こちらの記事に書いております。
ご興味のある方は、ぜひどうぞ。
今回は、そのまま行きます。
「Injected Provider」に設定し、「Goerli」になっていることを確認します。
なっていない場合は、メタマスクから、「Goerli」に変更します。
「Deploy」を選択します。
メタマスクが立ち上がり、実行後、下のように、コントラクトができました。
できたコントラクトアドレスを下のように、コピーします。
次に、下のように、「Verify」を行います。
「Verify」のやり方がご不明な場合は、こちらをご参照ください。
「Etherscan」を確認すると、下のように、「Verify」ができていることが確認できました。
2 Louperでの確認
では、「Louper」のサイトに行ってみましょう。
ここで、簡単にコントラクトを確認したり、「Facet」を追加することができます。
では、先ほどのコントラクトアドレスを入れて、検索してみましょう。
このように、結果が出てきました。
少し下を見てみると、関数名とそれに対応する関数セレクタが表示されています。
関数セレクタがご不明の場合は、こちらの第4章をご確認ください。
念の為、Keccak256で関数セレクタを確認すると、先ほどの値と合致していることも確認できました。
3 Facetコントラクトを作ってみよう①
1 コントラクトの作成
では、次に、「Remix」に戻り、「MessageFaucet.sol」を開きます。
こちらはメッセージを保管し、表示するだけのコントラクトです。
これを「Facet」として、「Diamond」に追加します。
下のように、コンパイルを行います。
続いて、「Goerli」であることを確認の上、「Deploy」を行います。
下のように、「Verify」も行います。
2 Facetの追加
Verifyが終わりましたら、「Louper」で「ADD NEW FACET」を選択します。
下からウォレット接続を行います。
「FETCH FACET INFO」でFacetの情報を取得します。
次に、追加したい関数にチェックを入れて、「ADD FACET」を選択します。
ここでは、全ての関数にチェックを入れています。
しばらくすると、下のように、「Success」となります。
「Upgrade History」を確認すると、下のように、「Diamond Cut」というイベントが発生しています。
これで、Facetが追加されたことが確認できました。
反映されていない場合は、ページの更新を行うと出てくると思います。
実際の追加したFacetも確認してみましょう。
「WRITE」で値を入力していきます。
ウォレット接続を行います。
「setMessage」を選択し、任意の文字を入力して、「EXECUTE」
しばらくすると、このように、「Success」となりました。
では、「READ」で読み込んでみましょう。
「getMessage」を選択します。
「READ」を押すと、先ほどの入力した文字が表示されました。
ここで、構成の話に少し戻ります。
下のように、データが保存されるのは、「Diamond」コントラクトでした。
そのため、「Facet」コントラクトにはデータが保存されていないことも確認してみましょう。
下のように、「Remix」で作成した「Facet」のコントラクトの「getMessage」を選択します。
すると、下のように、「0」となっており、入力した文字が反映されていないことが確認できます。
このように、「Facet」にはデータが保存されていないことが確認できました。
4 Facetコントラクトを作ってみよう②
1 コントラクトの作成
次に、せっかくなので、もう一つ、「Facet」を追加します。
こちらは数字を格納し、表示するコントラクトです。
「CalculateFacet.sol」を選択します。
下のように、コンパイルを行います。
Goerliであることを確認の上、「Deploy」を行います。
下のように、「Verify」を行います。
2 Facetの追加
では、「Louper」に戻り、「ADD NEW FACET」を選択します。
下のように、ウォレット接続を行います。
上でできた、コントラクトアドレスを貼り付け、「FETCH FACET INFO」を選択します。
追加したい関数にチェックを入れ、「ADD FACET」を選択
ここでは、全てにチェックを入れています。
では「CalculateFacet」コントラクトに書き込みを行ってみましょう。
「WRITE」を選択
下で、ウォレット接続を行います。
「setNumber」を選択
任意の数値を入力して、「EXECUTE」
下のように、「Success」となりました。
では、「READ」で読み取りを行いましょう。
「getNumber」で「READ」を押すと、下のように入力した値が表示されました。
先ほどと同様に、「Remix」のコントラクトで確認すると、こちらは値が更新されていないことも確認できました。
また、「Upgrade Histroy」を確認すると、下のように、「Diamond Cut」のイベントが発生していることも確認できました。
(ない場合は、更新を行うと、出てくると思います。)
5 Diamondコントラクトを確認しよう
では、ここで、「Diamond」コントラクトの中身も見てみましょう。
「READ」から進んでいきます。
「facetAddresses」を確認すると、下のように、「Diamond」も含めた、「Facet」のアドレスを確認することができます。
次に、「facets」関数を確認すると、「facet」コントラクトにどのような関数セレクタが含まれているのかを確認することができます。
「facetAddress」関数では、特定の関数セレクタがどの「Facet」コントラクトに含まれているのかを確認できます。
「facetFunctionSelectors」関数では、特定の「Facet」コントラクトに含まれる関数セレクタを確認することができます。
このように、「Diamond」コントラクトは関連づけている「Facet」コントラクトとその関数セレクタについての関数を提供していました。
6 Etherscanについて
なお、最後に「Etherscan」も確認してみましょう。
結論としては、この処理はやらない方が良いのではと考えています。
下のように、このコントラクトは「Proxy」コントラクトではないかと言われています。(実際、Proxyコントラクトです。)
「More Options」から「Is this a proxy?」へ進みます。
ここで、「Verify」を選択します。
すると、実装コントラクトの候補が表示されます。
これは、先ほど「Facet」として追加した、「CalculateFacet」コントラクトです。
「Save」を選択します。
すると、成功したようですので、元のページに戻ってみます。
すると、このように、「Read as Proxy」、「Write as Proxy」が追加されました。
これで「CalculateFacet」コントラクトの関数が見えるようになりました。
一方、本来、「Facet」はこの一つではなく、「MessageFacet」というコントラクトもありました。
しかし、記事執筆時点では、「Etherscan」では一つしか追加することができなさそうです。
となると、この「Diamond」には「CalculateFacet」という「Facet」しかないのではとミスリードしてしまう可能性もありそうだと思いました。
そのため、個人的には、「Etherscan」では、「Proxy」コントラクトの追加まではあえてしなくても良いのではと感じました。
今回は以上です。