Optimistic rollups で Ethereum のガス代が節玄出来るかを怜蚎する

前回は → Ethereum のスマヌトコントラクトで察戊ゲヌムを䜜るずどうなるか

1. Ethereum のガス代は䜕故高いのか

前回の蚘事で、dApps察戊ゲヌムを䜜成し、Ethereumの Test Networkにデプロむしお分かった事は、 その法倖ず蚀っおも差支えないでしょうな手数料の存圚です。 「1回10円もいかないだろう」ずか思っおたしたが甘かったです。 たさか数千円ずは・・・。

䞀瞬、担がれおるのかずも思いたしたが、珟状は本圓にそんな感じみたいです。 それだけの手数料を支払っお取匕がなされおいるようです。

なぜこんなにも手数料が高いのでしょうか

前回も説明したしたが、Ethereum の Gas Price は垂堎原理に基づいお盞堎が圢成されおいたす。 ネットワヌクのトランザクション凊理胜力に限界がある䞭で、より高い Gas Price を提瀺したトランザクションが優先凊理されるため、 玠早く取匕を終えたいナヌザヌは、平均よりも高い䟡栌を提瀺しおトランザクションを䜜成したす。 党く割に合わないくらいの Gas Price になれば取匕自䜓をやめるナヌザヌも出お来るでしょうが、 Gas Price は䞀向に䞋がらないので、それだけの手数料を問題にしない取匕が盞圓数存圚する蚌拠でもありたす。

この問題の䞭心ずなっおいる事情は、ネットワヌクの凊理胜力に䟛絊限界がある䞭で需芁が爆増しおいる事です。 凊理胜力が乏しいのは、珟圚採甚されおいる合意圢成アルゎリズムの仕組みに原因がありたす。

珟圚採甚されおいるのは PoWProof of Workずいう蚈算量が倚いアルゎリズムです。 このアルゎリズムでは、ネットワヌク党䜓で凊理できるトランザクション数が、1秒圓たり数十件皋床にしかなりたせん。 そしおたずいこずに、ブロックチェヌンの分散ネットワヌクは、改竄を防止するセキュリティ胜力が高くずも、 蚈算性胜を拡倧するスケヌリングに関しおは党く寄䞎したせん。ノヌドがいくら増えようが性胜はほずんど倉わりたせん。

さお、この状況はマむナヌにずっおうれしい悲鳎です。 ガス代はそのたたマむナヌぞの報酬に充おられるため、Gas Price が高ければ高い皋、ノヌドを動かし続けるメリットが高たりたす。 マむナヌにずっおは、トランザクションの凊理にかかる時間が1分だろうが10分だろが、盎接的には関係ないので 需芁が高たっおいるなら䟛絊を増やさずに䟡栌を釣り䞊げたほうが埗策です。 そもそもネットワヌク的な凊理限界に達しおいるので、マむナヌ自身では䟛絊を増やしようもないのですが。

たずめるず、

  1. 手数料は事実䞊の時䟡であり、垂堎原理に基づいお増枛する
  2. ネットワヌクの凊理胜力は限界があり、䟛絊は簡単に増やせない仕組みである
  3. 暗号資産ブヌムで需芁が爆増しおおり、倚少の手数料は問題ずならないくらいの䟡倀が流通しおいる

この3぀の芁因により、䞀般庶民には䞍圓ず思えるような䟡倀芳たでガス代が高隰しおいるのです。

1.1. ガス代が決たる仕組み

より高い Gas Price を提瀺したトランザクションが優先しお凊理されるず説明したした。 では、Gas Price はどのように決たるのでしょうか。 ほずんどの人は「ガ゜リンスタンド」を想像し、その時々の倀段が掲瀺されるのだず考えるでしょう。カヌド䌚員は〇〇円/Lみたいな感じで。

実際は、そのような倀段は掲瀺されたせん。倀段はナヌザヌが奜きに決められたす。 1 Unit あたり 1 Gwei でトランザクションを䜜るこずもできたすし、1 Unit あたり 1,000 Gwei1 szaboで䜜るこずもできたす。

問題は、冒頭で述べた「優先凊理」です。 倀段を決めるためには、珟圚どんな Gas Price のトランザクションが、ネットワヌク䞊にいく぀存圚し、 䟡栌垯によっおどれぐらいの埅ち時間でトランザクションが完了するのか知る必芁がありたす。

その為、ネットワヌクでは珟圚の Gas Price を掚定する「Gas Price Oracle」ずいう機胜を提䟛しおいたす。 ブロックチェヌンを過去に向かっお走査し、盎近の取匕から珟圚有効ず思われる Gas Price を掚定したす。 ナヌザヌはそれをもずに Gas Price を蚭定しおトランザクションを䜜成するわけです。

ブロックチェヌンの情報は誰でも閲芧できるので、Web䞊では Gas Price を教えおくれるサヌビスがいく぀もあり、 代衚的なのは Etherscan の Ethereum Gas Tracker です。

Low Speed、Average、High Speed の3段階に分けお Gas Price を提案しおくれたす。取匕の芁件に応じお Gas Price を遞びたしょう。

ちなみに、飛び抜けお高い Gas Price を蚭定したからず蚀っお プレミアムなサヌビスが受けられるわけではありたせん。

1.2. ガス代をケチるずどうなるか

「別に時間が掛かっおもいいんですよ。そんなに急いでないし」ずいう状況なら、ガス代を 1 Gwei に蚭定すればいいのでは、ず思う方もいるでしょう。

それに぀いおは Etherscan で面癜いものが芋られたす。 Pending Transaction を芋おみたしょう。 歀凊には面構えの違うトランザクション達が䞊びたす。極小の Gas Price でトランザクションを通すべく、4日も5日も埅機し続けるトランザクション達が。

「Last Seen」列の (!) にマりスオヌバヌする事でペンディングされおいる日数がわかりたす。

このように安すぎる Gas Price のトランザクションは埌回しにされ続け、い぀たで経っおも凊理されないずいう状況に陥りたす。 これらのトランザクションが䜕時凊理されるのかは、䞀切の保蚌がありたせん。

い぀か凊理されるかもしれないし、されないかもしれない。 ここ最近の混み具合だず、されない可胜性が極めお高いかも

ずころで、あなたのアカりントにペンディングされた状態のトランザクションがある堎合、そのアカりントでは、それ以降の劂䜕なるトランザクションもペンディングされたす。 トランザクションには nonce ずいう通し番号が振られ、必ず nonce の順番に凊理されたすから、より前の nonce のトランザクションが承認されるたで、 Ethereum ネットワヌクで取匕するこずが出来なくなりたす。

ただし、この状況を抜け出す術が無い蚳ではなく、同じ nonce でトランザクションを䞊曞きするこずが出来たす。 埓っお、同じ取匕内容を同じ nonce で、Gas Price だけ増額したトランザクションを発行するか、 0 ETHを送金するトランザクションに差し替えおキャンセルするず蚀った方法で状況を打開しおください。

たずめるず、Ethereum のネットワヌクでうたくやっおいくには、Gas Price は適切な倀に蚭定したしょう、ずいう事です。

1.3. ガス代はもう安くならないの

ブロックチェヌンに状態をコミットする際のガス代は避けられたせん。 これは倉えられない倧自然の摂理です。

しかし、将来的に Ethereum 2.0 で合意圢成アルゎリズムが倉曎され、シャヌディングも導入されるため、トランザクション凊理胜力が飛躍的に向䞊するずされおいたす。

盎近の改善ずしおは、7月頃に予定されおいるアップデヌト「ロンドン」ず呌ばれおいるで実装される、ネットワヌクの蟌み具合で䞊䞋する暙準の手数料が挙げられたす。 たた、ガス代ずしお支払われた Ether の䞀郚が焌华されるこずによっお、マむナヌに支払われる報酬が抑えられる事になりたす。

最終的にどれぐらいたで䞋がるのかは分かりたせんが、珟実的な範囲たで萜ずし蟌むこずを目指しおいるでしょうから、筆者はある皋床の期埅をしおいたす。

2. Ethereum のガス代を節玄する方法

ずは蚀え、dApps を開発するうえで、盎近のガス代問題を解決する方法を暡玢しなければなりたせん。 倚くの堎合、短期的な「ずりあえずの」解決策を導入しおいたす。

そのすべおが、Ethereum の倖にトランザクションをオフロヌドする「レむダヌ2」ず呌ばれる手法です。

2.1. 代替の方法で远及されるのは「セキュリティの確保」

技術的に蚀えば、レむダヌ2で凊理しお結果を「レむダヌ1Ethereum ブロックチェヌン」に反映するこず自䜓は、 幟らでも・どの様にでも実装可胜で、技術的な課題はありたせん。

問題はセキュリティであり、ブロックチェヌンが「トラストレス盞手を信甚する必芁がない事」を前提ずしおいる点です。 ナヌザヌは䞍正をするかもしれないずいう性悪説に立った仕組みにしなければなりたせん。

埓っお、党おの技術的課題は「劂䜕に䞍正を防いで取匕履歎の正圓性を保぀かあるいは劥協するか」に有りたす。

2.2. チャンネル

参加者 A ず B の間に専甚の取匕チャンネルを開きたす。 チャンネルの実装はオフチェヌンEthereum 倖で行われ、チャンネルが開かれおいる間はその䞭だけで自由に取匕を展開できたす。 チャンネルを開く際に Ether やトヌクン等の資産をコントラクトにデポゞットロックし、閉じる時Ethereum に結果を反映する時に枅算するため、 最䜎でも 2 トランザクション分のガス代がかかりたす。 片方が䞍正行為を働いた堎合は、もう片方が䞍正を蚌明する事でコントラクトにロックされた資産を取り戻すこずが出来たす。 ぀たり、これがセキュリティずなり、䞍正をするこずには、䞍正した偎がデポゞットした資産を倱うずいうリスクを負う恰奜になりたす。

蚀うたでもない事ですが、結果を反映するには参加者䞡名の同意が必芁です。 他の暗号資産䟋Bitcoinの堎合は、マルチシグずいう、アクセスに耇数の眲名を必芁ずするアカりントを甚いお党参加者の同意を埗るのですが、 Ethereum の堎合はマルチシグの機胜がない為、スマヌトコントラクトで代甚したす。

公匏の解説

2.3. サむドチェヌン

サむドチェヌンは、メむンチェヌンずなる Ethereum ブロックチェヌンず互換性を持぀ブロックチェヌンを別に䜜る手法です。

メむンチェヌン䞊でサむドチェヌンに転送したい資産をロックし、サむドチェヌン䞊で任意のトランザクションを安䟡な手数料を支払っお展開したす。 最終的に資産をメむンチェヌンぞ戻す堎合は、サむドチェヌンで資産を焌华し、焌华した蚌明をメむンチェヌンに提出するこずで、ロックされた資産が解攟されたす。

ブリッゞずなるメむンチェヌン䞊のコントラクトがすべおの資産の出入りを監芖したすから、蟻耄が合う仕組みです。

トヌクン等の資産は必ずメむンチェヌンずサむドチェヌン間で1察1のマッピングがされおいなければなりたせん。 ぀たり、サむドチェヌンだけで資産が生み出されるこずはありたせん。

メむンチェヌンから芋たセキュリティは資産の量的蟻耄の保蚌だけで、取匕の内容぀たりサむドチェヌン内のトランザクションには関知したせん。 利甚者からみたセキュリティは、サむドチェヌンが採甚する合意圢成アルゎリズムずネットワヌク運甚に巊右されたす。 サむドチェヌンの運営は、ほずんどが䞭倮集暩的ずなりたす。

公匏の解説

2.4. Plasma

Ethereum のブロックチェヌンをルヌトチェヌンずし、その子ずなるサブチェヌンPlasmaチェヌンを䜜成し、 本来ルヌトチェヌンで凊理されるべきトランザクションをPlasmaチェヌンにオフロヌドしたす。

ここたではサむドチェヌンず同じですが、 ブロックヘッダヌによるマヌクル朚を構成するこずにより、Plasmaチェヌンの䞋に曎にPlasmaチェヌンを䜜るこずが出来たす。 Plasmaチェヌンからはルヌトチェヌンぞは負荷をかけない様に控えめにブロックヘッダヌを通知するので、セキュリティがルヌトチェヌンによっお保護されたす。

Plasmaチェヌンの実䜓はカスタムされたブロックチェヌンプログラムであり、任意の機胜を実装できたす。

サむトチェヌンずの違いは、垞にルヌトチェヌンに報告するこずでセキュリティを確保しおいる点ず、 ルヌトチェヌンEthereumで動かしおいるスマヌトコントラクトがそっくりそのたた動くわけではないずいう点です。

ルヌトチェヌンに資産を戻す堎合は、異議申し立お期間ずしお、数日7日から14日を芁したす。 䞍正があった堎合は、䞍正の蚌拠をルヌトチェヌンに提出するこずで、䞍正ブロックの盎前に巻き戻したす。

䞊蚘は非垞にざっくりずした Plasma フレヌムワヌクの説明ですが、その実装は倚様に存圚したす。 Plasma ずいう固有の実装はありたせん。

掛かるガス代は実装によっおたちたちですが、最䜎でもPlasmaチェヌンぞの資産移行ずルヌトチェヌンぞの匕出し申請にそれぞれ1回ず぀、 異議申立期間埌の匕出しを含めお、合蚈3回分のガス代が発生したす。 たた、Plasmaチェヌンにおいおも非垞に安䟡だずは思いたすが幟蚱かの利甚料が掛かる事でしょう。

セキュリティは、ルヌトチェヌンに担保されたすが、Plasmaチェヌンの運営には䞭倮集暩的組織が関わるこずずなりたす。

公匏の解説

2.5. Rollups

Rollups は簡単に蚀えば、1 回に倚くのトランザクションを詰め蟌んで送信し、メむンチェヌンの Rollups コントラクト内で分解しお怜蚌させる事です。 トランザクションの実行はレむダヌ2ずなるオフチェヌンたたはサむドチェヌンで行い、状態を Rollups コントラクトに通知したす。 ナヌザヌが資産をレむダヌ2に移動させる必芁があるのは、これたでの技法ず同じです。 ぀たりレむダヌ2ぞの移行ず匕出しの最䜎 2 回は正芏のガス代が掛かりたす。

Rollups を実珟をするために 2 皮類のナヌザヌが必芁ずなりたす。1 ぀はトランザクタで、もう 1 ぀がリレむダヌアグリゲヌタヌずもです。 トランザクタは、その名の通りトランザクションを䜜成しおネットワヌクに送信したす。 リレむダヌは倚くのトランザクションを 1 ぀に纏め䞊げる「ロヌルアップ」を実行したす。 トランザクタはリレむダヌに手数料を支払い、リレむダヌが Ethereum のガス代を払いたす。 耇数のトランザクションを䞀手に匕き受けるリレむダヌが払うガス代は安くないのですが、 仮に 1 回のロヌルアップで数癟件のトランザクションを纏められれば、 トランザクション 1 ぀にかかるガス代は単䜓で支払うよりも安くなりたす。 たた、リレむダヌには誰でもなれたすが、䞍正を防止する芳点から、リレむダヌずなるには䞀定の資産を Rollups コントラクトに預ける必芁がありたす。

珟圚、Rollups にはセキュリティの実装に違いがある二通りの皮類がありたす。 れロ知識蚌明Zero knowledge、ZK ずもRollupsず、楜芳的OptimisticRollupsです。 前者はトランザクション毎に Rollups コントラクトで怜蚌を行う方匏で、 埌者はトランザクションを楜芳的に取扱い、毎床の怜蚌は行わず、結果に異議が出た堎合のみ怜蚌を行う方匏です。

Optimistic rollups は CALLDATA ず呌ばれるコントラクト呌び出しデヌタを、 そのたたメむンチェヌンに保存しおいるだけなので実装にフレキシビリティがあり、 OVM ずいう EVM 互換の仮想マシンが開発されたため、レむダヌ2で任意のコントラクトを実行出来るようになりたした。

ただし、怜蚌は行われおいないので、レむダヌ1に資産を匕き出す際、Plasmaず同様に異議申し立おの期間1週間を蚭ける必芁がありたす。 異議申し立おには、メむンチェヌンに保存された CALLDATA を再床実行しお同じ結果になるかあるいは異なるかを蚌明する方法が甚いられたす。

ZK rollups は、今のずころ簡単な転送や、前もっお決められた凊理に限られたすが、 トランザクションは垞に怜蚌枈みなので、資産を玠早くレむダヌ1に戻す事が出来たす。 たた、今埌技術開発が進めば、ZK rollups でも任意のコントラクトを実行できる可胜性がありたす。

公匏の解説

3. どれが䞀番ベストか

単に 1 回だけの取匕を考える堎合、どの様な手段を取ったずしおも同じだけガス代が掛かりたす。 スケヌルメリットが生たれるのは、䜕床も取匕する事を想定した堎合です。 䞀旊資産をデポゞットしおしたえば、埌はレむダヌ2で凊理する事により、安䟡な手数料で繰り返し取匕するこずが出来たす。

翻っお眺めおみれば、セキュリティの違いだけで、どれも方法ずしおは䞀長䞀短ずいった印象です。

これらの䞭で、最も Ethereum ネットワヌクの堅牢性の恩恵を受けられるのは ZK rollups だず思いたす。

察しお最もスケヌルするのはサむドチェヌン、たたはPlasmaです。 レむダヌ2を劂䜕様にも実装できるので、倧手決枈䌚瀟䞊みのスルヌプットを実珟するこずも可胜です。 気前が良ければ手数料を只にするこずもできたす。

しかし、サむドチェヌンは䞭倮集暩的で、基本的にEthereumのセキュリティから恩恵を受けられたせん。

チャンネルはそもそもオヌプン参加が出来ないので論倖ずしお、 Plasma は䞭倮集暩的で、途䞭のトランザクションデヌタがメむンチェヌンに公開されない可甚性が䜎いずいう問題があり、 ルヌトチェヌンに定期報告するず蚀った様な実装の手間も倚そうです。 Optimistic rollups は Plasma のデメリットを解消した方法ず蚀えたす。

ZK rollups は発展途䞊ず蚀った状況を螏たえるず、今のずころの最適解ずしおは「Optimistic rollups」かなず思いたした。

Optimistic rollups は実装がほが終わっおいたすが、メむンネットでの皌働は䞀郚のアプリケヌションに限定されおいたす。 テストネットは解攟されおいるので、今回は「Optimistic rollups」の導入を進めおみたいず思いたす。

DeJunkeng みたいなセキュリティなんおどうでもよさそうな dApps は「サむドチェヌン」でええやん、ず思われるかもしれたせんが、 DeJunkeng はあくたでもサンプルなので、「これはセキュリティが重芁なアプリである」ずいう前提で考えたす。

ず蚀うか、サむドチェヌンを䜿うなら普通の Web アプリずしお実装しお Firebase 蟺りにデプロむする・・・

4. DeJunkeng に導入しおみる

それでは実際に Optimistic rollups の実装である Optimistic Etehreum を䜿甚しお、 DeJunkeng をレむダヌ2にオフロヌドしおみたいず思いたす。

DeJunkeng は GitHub リポゞトリの master ブランチ から修正を加える想定で説明したす。

アヌキテクチャずしおは以䞋の様になりたす。

4.1. スマヌトコントラクトを修正

Optimistic rollups では、OVMずいうEVMEthereum Virtual Machine互換のバヌチャルマシンでスマヌトコントラクトを実行できたす。 これらは厳密に蚀えば違う物なので、EVM甚のSolidityコンパむラでコンパむルしたバむトコヌドは実行できたせん。 したがっお、レむダヌ2にデプロむするスマヌトコントラクトはOVM甚の専甚コンパむラでコンパむルしたす。

4.1.1. JunkCoinDepositedERC20.sol

たず、レむダヌ2 で JunkCoin トヌクンを管理するコントラクト JunkCoinDepositedERC20 を䜜成したす。

このコントラクトは、レむダヌ1 から移行された党おの JunkCoin を受け取っお管理したす。 ぀たり、党おのコむンをレむダヌ2でMint造幣するのではなく、レむダヌ1で発行しおレむダヌ2にデポゞットするずいう手法を取りたす。

準備ずしお以䞋のモゞュヌルずプラグむンを導入したす。

$ yarn add -D @eth-optimism/contracts @eth-optimism/plugins

hardhat.config.ts の冒頭import 文が䞊んでるずころに以䞋の䞀行を远加しおプラグむンを有効にしおください。

import "@eth-optimism/plugins/hardhat/compiler";

@eth-optimism/contracts には、Optimistic rollups に必芁なコントラクトがすべお同封されおいたす。

このモゞュヌルから Abs_L2DepositedToken を拝借しお以䞋の様なコントラクトを䜜成したした。

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import { JunkCoinERC20 } from "./JunkCoinERC20.sol";
import { Abs_L2DepositedToken } from "@eth-optimism/contracts/build/contracts/OVM/bridge/tokens/Abs_L2DepositedToken.sol";

/**
 * Runtime target: OVM
 */
contract JunkCoinDepositedERC20 is Abs_L2DepositedToken, JunkCoinERC20 {

    address admin;
    address dispenser;

    constructor(
        address _l2CrossDomainMessenger,
        string memory _name,
        string memory _symbol
    )
        Abs_L2DepositedToken(_l2CrossDomainMessenger)
        JunkCoinERC20(_name, _symbol, 0)
    {
        admin = msg.sender;
    }

    /**
     * Associate dispenser account/contract
     */
    function setDispenser(address _dispenser) external {
        require(msg.sender == admin, "Permission denied.");
        dispenser = _dispenser;
    }

    function _handleInitiateWithdrawal(
        address _to,
        uint _amount
    )
        internal
        override
    {
        require(msg.sender == _to || (dispenser != address(0) && msg.sender == dispenser), "Permission denied.");
        _burn(_to, _amount);
    }

    function _handleFinalizeDeposit(
        address _to,
        uint _amount
    )
        internal
        override
    {
        _mint(_to, _amount);
    }
}

トヌクンずしおの機胜は党お、元ずなる JunkCoinERC20 から継承しおいたす。 ただし Mint は行いたくないので、コンストラクタで initialSupply を指定できるようにしお 0 を蚭定しおいたす。

dispenser は JunkCoin をコントラクトから盎接払い出せるアカりントたたはコントラクトを指定する為に蚭けたした。 埓っお、レむダヌ1 に JunkCoin を転送できるのは JunkCoin の持ち䞻か、あるいは dispenser で指定するアカりントないしコントラクトのみずなりたす。

_l2CrossDomainMessenger は、レむダヌ1 ず通信を行うための Optimistic rollups 組み蟌みコントラクトです。

4.1.2. JunkCoinERC20.sol

JunkCoinERC20 は以䞋の様に修正したした。 コンストラクタで蚭定項目を泚入できるようにしただけですね。 尚、こちらのコントラクトはレむダヌ1にデプロむされたす。

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/**
 * Runtime target: EVM
 */
contract JunkCoinERC20 is ERC20 {

    constructor(string memory _name, string memory _symbol, uint _initialSupplies) ERC20(_name, _symbol) {
        _mint(msg.sender, _initialSupplies);
    }

    function decimals() public view override returns (uint8) {
        return 0;
    }
}

4.1.3. Junkeng.sol

驚くべきこずに Junkeng コントラクトには ほずんど倉曎の必芁がありたせん。

UX を考慮し、ゲットした JunkCoin を匕き出す関数「withdraw」に改造を加え、 レむダヌ2 のりォレットではなく、レむダヌ1 のりォレットに盎接匕き出せるように曞き換えたす。

import { iOVM_L2DepositedToken } from "@eth-optimism/contracts/build/contracts/iOVM/bridge/tokens/iOVM_L2DepositedToken.sol";

contract Junkeng {
    // ... äž­ç•¥ ...

    /**
     * Withdraw JunkCoin
     */
    function withdraw() public timeout haveCoins phaseAdvance {
        uint amount = coinStock[msg.sender];
        coinStock[msg.sender] = 0;
        IERC20(coin).transferFrom(admin, msg.sender, amount);

        iOVM_L2DepositedToken(coin).withdrawTo(msg.sender, amount);

        emit Withdrew(msg.sender, amount);
    }

4.1.4. コンパむル

レむダヌ 1 のコントラクトはそのたた埓来通り npx hardhat compile でコンパむルすれば OK ですが、 レむダヌ 2 は専甚の OVM コンパむラでバむトコヌドに倉換する必芁がありたす。

その為に TARGET=ovm ずいう環境倉数を蚭定しなければなりたせん。 環境倉数のOS䟝存を無くすため cross-env を導入したす。

$ yarn add -D cross-env

コンパむル甚の以䞋のコマンドを package.json に远加したした。 frontend 自䜓はレむダヌ2で動䜜させるため、レむダヌ1のコントラクトがtypechain自動生成されるTypeScript甚の型定矩ファむルを汚染しない様に、 --no-typechain オプションを指定したす。

  "scripts": {
    ...
    "compile": "npm run compile:evm && npm run compile:ovm",
    "compile:evm": "npx hardhat compile --no-typechain",
    "compile:ovm": "cross-env TARGET=ovm npx hardhat compile",

yarn compile でレむダヌ1甚・レむダヌ2甚が䞀床にコンパむル出来、 yarn compile:evm でレむダヌ1EVM甚、yarn compile:ovm でレむダヌ2OVM甚が個別にコンパむル出来たす。

4.2. 単䜓テスト

珟圚の hardhat で耇数レむダヌ構成のブロックチェヌンを取り扱うのは難しいので、 テストはモックを甚いお hardhat local network の単䞀レむダヌで動䜜するようにしお単䜓テストを実斜したす。

OVM でコンパむルする様に説明しずいおなんですが、バむトコヌドは EVM 甚を䜿甚したす。

deploy/hardhat-deployプラグむン甚にデプロむスクリプトは眮けないので、テストコヌド内に専甚のデプロむコヌドを曞きたす。

必芁ずなるモックは mockOVM_CrossDomainMessenger で、@eth-optimism/contracts に含たれおいたす。 以䞋はテスト環境をセットアップするコヌドです。

import { ContractFactory } from "ethers";
import {getContractFactory} from "@eth-optimism/contracts";
import {TransactionResponse} from "@ethersproject/abstract-provider";
import hre from "hardhat";

import * as JunkCoinERC20 from '../artifacts/contracts/JunkCoinERC20.sol/JunkCoinERC20.json';
import * as JunkCoinDepositedERC20 from '../artifacts/contracts/JunkCoinDepositedERC20.sol/JunkCoinDepositedERC20.json';
import * as Junkeng from '../artifacts/contracts/Junkeng.sol/Junkeng.json';


export const setupLocal = hre.deployments.createFixture(async () => {
    const { deployer } = await hre.ethers.getNamedSigners();

    // Mock of CrossDomainMessenger
    const L1CrossDomainMessengerFactory = getContractFactory('mockOVM_CrossDomainMessenger', deployer);
    const L1CrossDomainMessenger = await L1CrossDomainMessengerFactory.deploy(0);
    await L1CrossDomainMessenger.deployTransaction.wait();
    console.log('(L1)CrossDomainMessenger: ' + L1CrossDomainMessenger.address);

    const L2CrossDomainMessengerFactory = getContractFactory('mockOVM_CrossDomainMessenger', deployer);
    const L2CrossDomainMessenger = await L2CrossDomainMessengerFactory.deploy(0);
    await L2CrossDomainMessenger.deployTransaction.wait();

    await L1CrossDomainMessenger.setTargetMessengerAddress(L2CrossDomainMessenger.address)
        .then((tx: TransactionResponse) => tx.wait());
    await L2CrossDomainMessenger.setTargetMessengerAddress(L1CrossDomainMessenger.address)
        .then((tx: TransactionResponse) => tx.wait());
    console.log('(L2)CrossDomainMessenger: ' + L2CrossDomainMessenger.address);

    // Deploy L1 contracts
    const L1JunkCoinERC20Factory = new ContractFactory(JunkCoinERC20.abi, JunkCoinERC20.bytecode, deployer);
    const L1JunkCoinERC20 = await L1JunkCoinERC20Factory.deploy(
        "JunkCoin",
        "JKC",
        100000000,
    )
    await L1JunkCoinERC20.deployTransaction.wait();
    console.log('(L1)JunkCoinERC20: ' + L1JunkCoinERC20.address);

    // Deploy L2 contracts
    const L2JunkCoinDepositedERC20Factory = new ContractFactory(JunkCoinDepositedERC20.abi, JunkCoinDepositedERC20.bytecode, deployer);
    const L2JunkCoinDepositedERC20 = await L2JunkCoinDepositedERC20Factory.deploy(
        L2CrossDomainMessenger.address,
        "JunkCoin",
        "JKC"
    )
    await L2JunkCoinDepositedERC20.deployTransaction.wait();
    console.log('(L2)JunkCoinDepositedERC20: ' + L2JunkCoinDepositedERC20.address);

    const L2JunkengFactory = new ContractFactory(Junkeng.abi, Junkeng.bytecode, deployer);
    const L2Junkeng = await L2JunkengFactory.deploy(
        L2JunkCoinDepositedERC20.address
    )
    await L2Junkeng.deployTransaction.wait();
    console.log('(L2)Junkeng: ' + L2Junkeng.address);

    // Deploy L1 contracts
    const L1ERC20GatewayFactory = getContractFactory('OVM_L1ERC20Gateway', deployer);
    const L1ERC20Gateway = await L1ERC20GatewayFactory.deploy(
        L1JunkCoinERC20.address,
        L2JunkCoinDepositedERC20.address,
        L1CrossDomainMessenger.address,
    )
    await L1ERC20Gateway.deployTransaction.wait();
    console.log('(L1)OVM_L1ERC20Gateway: ' + L1ERC20Gateway.address);

    // Init L2 contracts
    await L2JunkCoinDepositedERC20.init(L1ERC20Gateway.address, { gasLimit: 1000000 })
        .then((tx: TransactionResponse) => tx.wait());
    await L2JunkCoinDepositedERC20.increaseAllowance(L2Junkeng.address, 100000000, { gasLimit: 1000000 })
        .then((tx: TransactionResponse) => tx.wait());
    await L2JunkCoinDepositedERC20.setDispenser(L2Junkeng.address, { gasLimit: 1000000 })
        .then((tx: TransactionResponse) => tx.wait());
    console.log('(L2)JunkCoinDepositedERC20 has been initialized.');

    // Deposit full balance of token
    await L1JunkCoinERC20.increaseAllowance(L1ERC20Gateway.address, 100000000, { gasLimit: 1000000 })
        .then((tx: TransactionResponse) => tx.wait());
    await L1ERC20Gateway.deposit(100000000, { gasLimit: 1000000 })
        .then((tx: TransactionResponse) => tx.wait());
    await L2CrossDomainMessenger.relayNextMessage({ gasLimit: 9500000 })
        .then((tx: TransactionResponse) => tx.wait());
    console.log('(L1)JunkCoinERC20 has been deposited.');

    return {
        L1CrossDomainMessenger,
        L2CrossDomainMessenger,
        L1JunkCoinERC20,
        L2JunkCoinDepositedERC20,
        L2Junkeng,
        L1ERC20Gateway,
    }
})

@eth-optimism/contracts の型定矩ファむルがないので、プロゞェクトルヌトに index.d.ts を䜜っお

declare module "@eth-optimism/contracts" {
  import { ContractFactory, Signer } from 'ethers'

  export function getContractFactory(
      name: string,
      signer?: Signer,
      ovm?: boolean
  ): ContractFactory;
}

ず曞き蟌んでください。 たた、tsconfig.json の include に index.d.ts を远加しおください。

レむダヌ1 では、OVM_L1ERC20Gateway ずいうコントラクトで JunkCoinERC20 をラップしおいたすが、 このコントラクトが資産をロックしおレむダヌ2に転送したす。 圓然、レむダヌ2から戻っおきた資産を所有者アカりントに枡す圹割も担いたす。

mockOVM_CrossDomainMessenger はメッセヌゞを送信キュヌに入れるだけで、 本来はシステムが勝手にやっおくれるメッセヌゞの配信たではしおくれないので、 relayNextMessage を手動で呌び出しお、レむダヌ2のコントラクトここでは JunkCoinDepositedERC20 にメッセヌゞを着信させたす。

createFixture でラップしおいるので、テストコヌドの beforeEach で呌び出しおおけば、 2回目以降は hardhat がセットアップ枈みのスナップショットを埩元しおくれるので、 高速に単䜓テストを実行できたす。

テストコヌド偎では、

describe('Junkeng', () => {
    let contracts: {[name: string]: Contract };

    beforeEach(async () => {
        contracts = await setupLocal();
    })

ず曞いおおけばデプロむ枈みのコントラクトにアクセスできるようになりたす。

党おの単䜓テストコヌドは こちら にありたす。

yarn test でテストが実行できるように package.json に以䞋を远加したした。

"scripts": {
    ...
    "test": "npm run compile:evm && npx hardhat test --no-compile --network hardhat",

4.3. デプロむスクリプトの䜜成

フロント゚ンド甚に frontend/src/hardhat/deployments を自動生成したいので、hardhat-deploy は匕き続き䜿甚したす。 ただし、hardhat-deploy がデプロむ察象ずするのはレむダヌ2のみにしたす。 レむダヌ1のコントラクトは hardhat-deploy の管理倖でデプロむしお、コントラクトアドレスだけ䜿甚したす。

たた、hardhat-deploy の OVM コンパむラ察応は䞍完党で、OVM 甚のバむトコヌドを匕っ匵っお来ない様なので、 自前で artifactsコンパむラの生成物をむンポヌトしおデプロむしたす。

以䞋は、実際のデプロむスクリプト deploy/Junkeng.ts です。

import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';
import assert from "assert";
import {JsonRpcProvider} from "@ethersproject/providers";
import {Contract, ContractFactory, Wallet} from "ethers";
import {getContractFactory} from "@eth-optimism/contracts";
import {TransactionResponse} from "@ethersproject/abstract-provider";

import * as JunkCoinERC20 from "../artifacts/contracts/JunkCoinERC20.sol/JunkCoinERC20.json";
import * as JunkCoinDepositedERC20 from '../artifacts-ovm/contracts/JunkCoinDepositedERC20.sol/JunkCoinDepositedERC20.json';
import * as Junkeng from '../artifacts-ovm/contracts/Junkeng.sol/Junkeng.json';


const deployL1JuncCoinERC20 = async (l1Wallet: Wallet): Promise<Contract> => {
    // Deploy L1 contracts
    const L1JunkCoinERC20Factory = new ContractFactory(JunkCoinERC20.abi, JunkCoinERC20.bytecode, l1Wallet);
    const L1JunkCoinERC20 = await L1JunkCoinERC20Factory.deploy(
        "JunkCoin",
        "JKC",
        100000000,
    )
    await L1JunkCoinERC20.deployTransaction.wait();
    console.log('(L1)JunkCoinERC20: ' + L1JunkCoinERC20.address);

    return L1JunkCoinERC20;
}

const deployL1ERC20Gateway = async (
    L1MessengerAddress: string,
    L1JunkCoinERC20Address: string,
    L2JunkCoinDepositedERC20Address:
    string, l1Wallet: Wallet
): Promise<Contract> => {
    // Deploy L1 contracts
    const L1ERC20GatewayFactory = getContractFactory('OVM_L1ERC20Gateway');
    const L1ERC20Gateway = await L1ERC20GatewayFactory.connect(l1Wallet).deploy(
        L1JunkCoinERC20Address,
        L2JunkCoinDepositedERC20Address,
        L1MessengerAddress,
    )
    await L1ERC20Gateway.deployTransaction.wait();
    console.log('(L1)OVM_L1ERC20Gateway: ' + L1ERC20Gateway.address);

    return L1ERC20Gateway;
}

// Deposit full balance of token
const depositL1JunkCoinERC20 = async (L1JunkCoinERC20: Contract, L1ERC20Gateway: Contract) => {
    await L1JunkCoinERC20.increaseAllowance(L1ERC20Gateway.address, 100000000, { gasLimit: 1000000 })
        .then((tx: TransactionResponse) => tx.wait());
    await L1ERC20Gateway.deposit(100000000, { gasLimit: 1000000 })
        .then((tx: TransactionResponse) => tx.wait());
    console.log('(L1)JunkCoinERC20 has been deposited.');
}


const deploy: DeployFunction = async function ({
    getNamedAccounts,
    deployments,
    getChainId,
    getUnnamedAccounts
}: HardhatRuntimeEnvironment) {
    assert(process.env.WALLET_PRIVATE_KEY_DEPLOYER);
    assert(process.env.L1_WEB3_URL);
    assert(process.env.L1_MESSENGER_ADDRESS);
    assert(process.env.L2_MESSENGER_ADDRESS);

    const { deploy, execute, save } = deployments;
    const { deployer } = await getNamedAccounts();

    const l1Provider = new JsonRpcProvider(process.env.L1_WEB3_URL);
    const l1Wallet = new Wallet(process.env.WALLET_PRIVATE_KEY_DEPLOYER, l1Provider);

    const L1JunkCoinERC20 = await deployL1JuncCoinERC20(l1Wallet);

    const L2JunkCoinDepositedERC20 = await deploy("JunkCoinDepositedERC20", {
        from: deployer,
        gasLimit: 8000000,
        args: [process.env.L2_MESSENGER_ADDRESS, "JunkCoin", "JKC"],
        contract: {
            abi: JunkCoinDepositedERC20.abi,
            bytecode: JunkCoinDepositedERC20.bytecode,
            deployedBytecode: JunkCoinDepositedERC20.deployedBytecode,
        },
        skipIfAlreadyDeployed: false,
    })
    console.log('(L2)JunkCoinDepositedERC20: ' + L2JunkCoinDepositedERC20.address);

    const L1ERC20Gateway = await deployL1ERC20Gateway(
        process.env.L1_MESSENGER_ADDRESS, L1JunkCoinERC20.address, L2JunkCoinDepositedERC20.address, l1Wallet);

    const junkeng = await deploy("Junkeng", {
        from: deployer,
        gasLimit: 8000000,
        args: [L2JunkCoinDepositedERC20.address],
        contract: {
            abi: Junkeng.abi,
            bytecode: Junkeng.bytecode,
            deployedBytecode: Junkeng.deployedBytecode,
        },
        skipIfAlreadyDeployed: false,
    })
    console.log('(L2)Junkeng: ' + junkeng.address);

    await execute("JunkCoinDepositedERC20", {from: deployer, gasLimit: 8000000}, 'init', L1ERC20Gateway.address);
    console.log('(L2)Executed JunkCoinDepositedERC20.init()');
    await execute("JunkCoinDepositedERC20", {from: deployer, gasLimit: 8000000}, 'approve', junkeng.address, '100000000');
    console.log('(L2)Executed JunkCoinDepositedERC20.approve()');
    await execute("JunkCoinDepositedERC20", {from: deployer, gasLimit: 8000000}, 'setDispenser', junkeng.address);
    console.log('(L2)Executed JunkCoinDepositedERC20.setDispenser()');

    await depositL1JunkCoinERC20(L1JunkCoinERC20, L1ERC20Gateway);
}

export default deploy;

単䜓テストの時に曞いたデプロむコヌドず䌌たような手順を螏んでいたすが、実際にレむダヌ1ずレむダヌ2で別々のブロックチェヌンにデプロむしおいたす。 これが非垞にややこしい

実際にデプロむする前には、

  1. WALLET_PRIVATE_KEY_DEPLOYER レむダヌ1ぞデプロむするずきに䜿うアカりントの秘密鍵
  2. L1_WEB3_URL レむダヌ1の JsonRpc URL
  3. L1_MESSENGER_ADDRESS レむダヌ1のCrossDomainMessengerコントラクトアドレス
  4. L2_MESSENGER_ADDRESS レむダヌ2のCrossDomainMessengerコントラクトアドレス

䞊蚘の環境倉数を必芁ずしたす。 .env を䜜っお指定しおください。

4.4. ロヌカルテスト環境ぞのデプロむ

Optimistic Ethereum では専甚のロヌカルテスト環境が甚意されおいたす。 git コマンドず Docker を䜿うため、予めむンストヌルをしおください。 めんどくさければ optimism-integration の導入を飛ばしお、蚭定ず Test Network ぞのデプロむに進んでください

4.4.1. optimism-integration の導入

GitHub からクロヌンしおセットアップしたす。

git clone git@github.com:ethereum-optimism/optimism-integration.git --recurse-submodules
cd optimism-integration
docker-compose pull

ノヌドを立ち䞊げるずきは以䞋のコマンドです。

$ ./up.sh

localhost:8545 にレむダヌ2、localhost:9545 にレむダヌ1のノヌドが起動したす。

尚、珟圚 optimism-integration は開発アクティビティが高いので、 たたに最新バヌゞョンが動䜜しなくなる事もあるかもしれたせん。

その堎合はもの凄いスピヌドで流れおいく゚ラヌメッセヌゞを確認しお、問題個所を特定しなければなりたせん 経隓䞊ですが、倧抵は docker-compose.env.yml の蚘述ミスが原因です。

4.4.2. 蚭定しおデプロむ

プロゞェクトルヌトにある dot.env を .env にリネヌムしお、内容を以䞋の様に修正しおください。 尚、りォレットの秘密鍵は、ノヌドを立ち䞊げたずきに衚瀺されるもので、レむダヌ1では 10000ETH が予め付䞎されたす。

# Default account on optimism-integration
WALLET_PRIVATE_KEY_DEPLOYER=0x754fde3f5e60ef2c7649061e06957c29017fe21032a8017132c0078e37f6193a
WALLET_PRIVATE_KEY_SEQUENCER=0xd2ab07f7c10ac88d5f86f1b4c1035d5195e81f27dbe62ad65e59cbf88205629b
WALLET_PRIVATE_KEY_TESTER1=0x23d9aeeaa08ab710a57972eb56fc711d9ab13afdecc92c89586e0150bfa380a6
WALLET_PRIVATE_KEY_TESTER2=0x5b1c2653250e5c580dcb4e51c2944455e144c57ebd6a0645bd359d2e69ca0f0c

# Default settings on optimism-integration
L1_WEB3_URL=http://localhost:9545
L2_WEB3_URL=http://localhost:8545
L1_MESSENGER_ADDRESS=0x6418E5Da52A3d7543d393ADD3Fa98B0795d27736
L2_MESSENGER_ADDRESS=0x4200000000000000000000000000000000000007

hardhat.config.ts の networks に layer1 ず layer2 を远加し、環境倉数を読み蟌みたす。

import dotenv from "dotenv";
dotenv.config();
// ... äž­ç•¥ ...

import assert from "assert";

assert(process.env.WALLET_PRIVATE_KEY_DEPLOYER);
assert(process.env.WALLET_PRIVATE_KEY_SEQUENCER);
assert(process.env.WALLET_PRIVATE_KEY_TESTER1);
assert(process.env.WALLET_PRIVATE_KEY_TESTER2);
assert(process.env.L1_WEB3_URL);
assert(process.env.L2_WEB3_URL);
assert(process.env.L1_MESSENGER_ADDRESS);
assert(process.env.L2_MESSENGER_ADDRESS);

// ... äž­ç•¥ ...

const config: HardhatUserConfig = {
    react: {
        providerPriority: ["web3modal", "hardhat"],
    },
    defaultNetwork: "layer2",
    networks: {
        // ... äž­ç•¥ ...
        layer2: {
            url: process.env.L2_WEB3_URL,
            accounts: [
                process.env.WALLET_PRIVATE_KEY_DEPLOYER,
                process.env.WALLET_PRIVATE_KEY_SEQUENCER,
                process.env.WALLET_PRIVATE_KEY_TESTER1,
                process.env.WALLET_PRIVATE_KEY_TESTER2,
            ],
        },
        layer1: {
            url: process.env.L1_WEB3_URL,
            accounts: [
                process.env.WALLET_PRIVATE_KEY_DEPLOYER,
                process.env.WALLET_PRIVATE_KEY_SEQUENCER,
                process.env.WALLET_PRIVATE_KEY_TESTER1,
                process.env.WALLET_PRIVATE_KEY_TESTER2,
            ],
        },

デプロむコマンドを package.json に仕蟌みたす。

  "scripts": {
    ...
    "deploy": "npm run compile:evm && cross-env TARGET=ovm npx hardhat deploy --reset --network layer2",

コマンドラむンからは yarn deploy で実行できたす。

network ずしお layer2 を指定しおいるのず、デプロむの再利甚をしない様に --reset を䜿甚しおいたす。 埓っお、デプロむを実行する床に、党コントラクトのアドレスが倉曎になりたす。

layer2 を指定しおいたすが、䟝存する layer1 のコントラクトも䞀緒にデプロむされたす。

frontend/src/hardhat/deployments が曎新され、フロント゚ンドにもコントラクトアドレスが反映されたす。

たた、デプロむ時に衚瀺されるコントラクトアドレスは、埌々の為にメモ垳などぞ控えおおいおください。

4.4.3. 動䜜確認

コン゜ヌルでコントラクトにアクセスしお動䜜確認をしおみたしょう。

$ npx hardhat console --no-compile --network layer2

hardhat コン゜ヌルが起動したら、以䞋の順でコマンドを打ち蟌んでください。

> const signers = await ethers.getSigners()
> const JunkCoinDepositedERC20 = await ethers.getContract('JunkCoinDepositedERC20', signers[0])
> (await JunkCoinDepositedERC20.totalSupply()).toNumber()
100000000

正垞にデプロむされ、JunkCoin がデポゞットされおいたすね。

4.4.4. フロント゚ンドからアクセス

幞運にしおフロント゚ンドには䜕も修正を加える必芁がありたせん。 単にプロゞェクトルヌトで、

$ yarn frontend

を実行しおテストサヌバヌを起動したしょう。

MetaMask でカスタムネットワヌクの远加が必芁になりたす。 以䞋の様に远加しおください。 必芁なのは layer2 だけで、layer1 は必須ではありたせん。

MetaMask で layer2 のネットワヌクに切り替えお Join game しおみたしょう。

ロヌカルで起動した layer2 ノヌドでは Gas Price を 0 にしおもトランザクションが通りたす。 埓っお ETH は必芁ありたせん

さお、きちんず動䜜したでしょうか。゚ラヌが出お進行しない堎合は、デプロむからやり盎しおみおください。 たた、アカりントのリセットMetaMask の Settings → Advanced → Reset Account も行っおください。

入手したコむンをレむダヌ1に匕き出すには、単に Withdraw をクリックしたす。 テスト甚のロヌカルノヌドでは、れロ遅延でレむダヌ1に転送されたす本来は1週間かかりたす

入手したコむンの残高を確認するには、デプロむ時に衚瀺された JunkCoinERC20 のアドレスを、Add Token で MetaMask に远加しおください。 その際、忘れずにネットワヌクを layer1 ぞ切り替えおください。

4.5. Test Network にデプロむ

Optimistic Ethereum では Kovan Test Network に接続されたテストネットが公開されおいるので、 レむダヌ2甚のスマヌトコントラクトをパブリックにデプロむ出来たす。

未だ Mainnet にはデプロむできたせん。

4.5.1. ETH の入手

前準備ずしお Kovan Test Network 䞊の ETH をこちら で入手しおください。 GitHub のアカりントが必芁です

4.5.2. ノヌドの準備

前回も䜿った Alchemy で Kovan Test Network のノヌドを立ち䞊げおアプリを䜜成しお API URL を取埗しおください。 レむダヌ1甚のコントラクトを Kovan Test Network ぞデプロむするのに必芁です。

4.5.3. .env の線集ずデプロむ実行

.evn を以䞋の様に線集しおください。

WALLET_PRIVATE_KEY_DEPLOYER=アカりントの秘密鍵に眮換Kovan Test Network 䞊で ETH が必芁です
WALLET_PRIVATE_KEY_SEQUENCER=アカりントの秘密鍵に眮換適圓なアカりント
WALLET_PRIVATE_KEY_TESTER1=アカりントの秘密鍵に眮換適圓なアカりント
WALLET_PRIVATE_KEY_TESTER2=アカりントの秘密鍵に眮換適圓なアカりント

L1_WEB3_URL=甚意した Kovan Test Network ノヌドの API URL
L2_WEB3_URL=https://kovan.optimism.io
L1_MESSENGER_ADDRESS=0xb89065D5eB05Cac554FDB11fC764C679b4202322
L2_MESSENGER_ADDRESS=0x4200000000000000000000000000000000000007

以䞊でデプロむの準備が敎いたした。 yarn deploy で実行です。

4.5.4. MetaMask の蚭定

Kovan Test Network はデフォルトでネットワヌクリストに含たれおいたす。 レむダヌ2 を以䞋の様に远加しおください。

あずは切り替えお䜿甚したす。

パブリックの Optimistic Ethereum テストサヌバヌも、Gas Price 0 でトランザクションを通すこずが出来たす。 実際は幟蚱かの手数料が掛かりたすが、非垞に安䟡になり、今の様な高隰が起こらないものず思われたす。

手数料は ETH で支払いたすが、その為の資金はレむダヌ1からデポゞットしおくる必芁があるでしょう今回はその手順は省かれおいたす 埓っお、デポゞット甚の UI を別途実装する必芁がありそうです。

テストサヌバヌでは、レむダヌ2からレむダヌ1に資産を持ち出そうずするず、埅ち時間が発生するようです 筆者のテストも埅ち時間䞭なので、どれくらいの時間掛かるのか確認できおない

5. 実装しおみた所感・問題点

5.1. 「タむムスタンプ」問題

こちら に曞かれおいるのですが、 DeJunkeng も時間に䟝存するdAppsです。 Optimistic rollups のレむダヌ2ではこれが深刻な問題で、block.timestamp に倧きな遅延が生じる為、 ゲヌム開始しおから5分間の制限時間を正確に枬るこずが出来たせん。

レむダヌ2 における block.timestamp は、レむダヌ1ぞトランザクションを最埌にロヌルアップした時間 に蚭定されるため、 実時間から最倧10分皋床のずれが生じたす。

Optimistic Ethereum のレむダヌ2には「ブロック」ずいう抂念は存圚せず、有るのは単なるトランザクションのリストです即ちブロックチェヌンですらない

もずもず Ethereum のスマヌトコントラクトにおける時間の扱いには課題があり、 ブロックの生成毎に曎新される block.timestamp の粟床は荒いもので、 これに䟝存する事は掚奚されおいたせんでした。

時間をセキュアに扱うためには、䞭倮集暩的なサヌバヌを甚意するか、別の゜リュヌションを考え出す必芁がありそうです。

5.2. トランザクション承認が早すぎるので想定倖の状態になる

これはフロント゚ンドのコヌドを盎せばいい話ですが、 トランザクションを発行しおからほがラグ無く承認されるので、 倉な状態に入るバグが生たれおしたいたした。

スルヌプットが高くなったが故に露芋したバグずいうや぀ですね。

5.3. レむダヌ2 の資産をレむダヌ1 に持ち出す際の埅ち時間

レむダヌ2にある資産をレむダヌ1に持ち出そうずするず、7日皋床の埅ち時間が発生するのは UX ずしお厳しいかもしれないです。 これが単なるEtherやERC20トヌクンず蚀った代替え可胜な資産の堎合は、匕き出そうずする資産を担保に同じものを短期貞付する圢で、 遅延を短くする仕組みが䜜れそうですが、NFT非代替性トヌクンのような䞀品物は難しいですね。

この埅ち時間䞭は、匕き出そうずする資産が消えたような状態になるので、「Ether・トヌクンは転送䞭です」ず分かる䜕らかの仕組みが必芁かなず思いたした。

5.4. MetaMask でネットワヌクを切り替えるのは面倒

MetaMask でカスタムネットワヌクを远加しおレむダヌ2に切り替えるずいう操䜜を必芁ずするのは UX ずしお厳しいかもしれないです。

間違ったネットワヌクを遞択しおいお気付かないずいう事も発生しお、問い合わせ頻床が爆増しそうです。

dApps のフロント゚ンドに独自のりォレットを実装しお、MetaMask は切り替えさせないずいうのが UX にずっおはベストな案かも。

5.5. 開発環境hardhatの察応は未完党

レむダヌ2 ず OVM コンパむラをもっず䞊手く扱えるように hardhat-deploy プラグむンの「プラグむン」を開発する必芁がありそうです。

5.6. 実際どれくらい安くなるかはやっおみないず分からない

Test Network では Gas Price 0 でトランザクションが通るので、実際幟らくらい掛かるのかは芋積もれたせんでした。 Optimistic rollups レむダヌ2 の䞻な維持コストは、システム維持費ずレむダヌ1ぞのロヌルアップ時に発生するガス代でしょうから、 利甚者が倚ければスケヌルメリットが埗られそうですが、今のずころ良くわからないですね。 しかしよく考えれば、レむダヌ2は誰でも立ち䞊げられる仕組みなので、レむダヌ2運営者が任意に料金を蚭定すればいいずいう話なのかもしれたせん。 そう考えるず、レむダヌ2で展開する dApps の収益性によっおは無料に出来る物もあるでしょう。

6. 今回はここたで

今回は、Ethereum に起こっおいる「ガス高隰問題」ぞの察策を怜蚎し぀぀、 珟圚進行圢で開発が進んでいるレむダヌ2技術の䞀぀「Optimistic rollups」を実際に䜿っおみるこずで玹介したした。

完成すれば Ethereum でセキュリティを確保したたた、安䟡な利甚料で dApps が䜿える独自プラットフォヌムを展開する有効な手段になりそうです。 匊瀟ずしおも匕き続き远っおいきたいず考えおいたす。

今回はここたでずしたす。それではたた

7. ゜ヌスコヌドは GitHub で公開䞭

MITラむセンスで公開䞭です。 ご利甚は自己責任でどうぞ。  optimism ブランチにアップされおいたす

バグなど芋぀けたら Issue たでどうぞ。