Solidityスマートコントラクトでガス料金を最適化する方法
このガイドでは、Solidityでスマートコントラクトを記述する際のガスコストを最適化する方法について、実践的なステップバイステップのウォークスルーを提供します。
なぜガスの最適化が重要なのか?
ガスの最適化は、スマート・コントラクトの開発において重要な部分である。 ネットワークが混雑しているときでも、スマートコントラクトが効率的でコスト効率に優れていることを保証するのに役立つ。 コントラクト実行の計算オーバーヘッドを削減することで、開発者は取引手数料を下げ、確認時間を短縮し、dApps全体のスケーラビリティを向上させることができる。
開発者にとって、ガスの最適化とは、不必要な計算を最小限に抑え、クリーンで、安全で、予測可能なコードを書く ことである。 ユーザーにとっては、過剰な料金を支払うことなく、契約とやり取りできるようにすることだ。
カイアで特に重要な理由
83を超えるMini dAppsがローンチされ、それを数えると、カイア・ブロックチェーンは、これらのオンチェーン・アプリケーションの爆発的な成長によって牽引され、取引量においてEVM互換チェーンをリードする存在となった。
各Mini dAppは、オンチェーンアクションを実行するためにスマートコントラクトに依存している。 アイテムの鋳造、賭け、ゲーム内アセットの管理など、あらゆる契約のやり取りでガスが消費される。 最適化しなければ、これらのdAppsはすぐに、特に規模が大きくなると、ユーザーがやりとりするには高すぎるものになりかねない。
だからこそ、ガス効率は単なるいいとこ取りではないのだ。 それは必要なことだ。 Kaiaで構築する開発者は、機能性とセキュリティを保ちながら、各関数呼び出しがコストを最小化するように最適化されていることを確認しなければならない。
ガス最適化技術
保管梱包
ブロックチェーン上のデータの保存と検索は、特にトランザクションやブロックをまたいでデータを永続させなければならない場合、最もガスコストのかかる作業の一つである。 Solidityでは、このデータは契約ストレージに保存され、永続的でガス代がかかる。 これらのコストを削減するために、開発者はストレージの使用方法、特にステート変数の宣言方法を慎重に最適化しなければならない。
カイア仮想マシン(KVM)は、契約データをストレージスロットと呼ばれる単位に格納する。 各ストレージスロットは、ちょうど256ビット(32バイト)のデータを保持することができる。 例えば、ブールは1バイト、アドレスは20バイトです。
ストレージ・パッキングと呼ばれる技術により、32バイトのストレージ・スロット1つに収まるように、より小さな変数を密に並べることができる。 複数のストレージスロットにアクセスするよりも、1つのストレージスロットから読み出したり、書き込んだりする方が圧倒的に安いからだ。
次の例を考えてみよう:
故障:*。
最適化されていないバージョン (SlotUnOptimized) では、Solidity は構造体を次のように格納します:
- 宛先アドレス→20バイト必要→スロット0に格納
- uint256 numConfirmations -> 32バイト必要 -> スロット1に格納
- uint80の値(10バイト)とboolの実行値(1バイト)→スロット2に格納
value変数とexecuted変数は小さいにもかかわらず、Solidityは明示的に並べ替えを行わない限り、アラインメントパディングにより、それらを独自のストレージスロットに配置します。 その結果、この構造では3つの貯蔵スロットを使用するため、貯蔵運転にかかるガス代は3倍になる。 しかし、address (20 byte) + uint80 (10 byte) + bool (1 byte)
の合計サイズは31 byteであり、シングルスロットの32 byte**の制限内に収まる。 小さな変数が一緒にグループ化されるように宣言を並べ替えるだけで、Solidityはそれらを同じスロットにまとめることができます。 これが収納パッキングの真髄だ。
上で見たように、最適化バージョン(*SlotOptimized)では、すべての小さな変数は互いに隣接して配置され、コンパイラーはそれらをより少ないスロットに格納することができます。
キャッシュ・ストレージ
ストレージスロットにどのように変数が配置されているかだけでなく、ストレージへのアクセスや変更に関連するガスコストを理解することも重要だ。
カイア仮想マシンの各ストレージスロットのコストは以下の通り: 初期化(最初の書き込み)に20,000ガス 更新(その後の書き込み)に5,000ガス このため、特に頻繁に呼び出される関数では、ストレージの直接の読み書きの回数を最小限に抑えることが重要です。 効果的なパターンの一つは、関数内で何度もアクセスする必要がある場合に、ストレージ変数をメモリにキャッ シュすることである。
次の例を考えてみよう:
変数のデフォルト値への初期化を避ける
Solidityでは、すべてのデータ型に事前に定義されたデフォルト値があります。 例えば、addressのデフォルトはaddress(0)、boolのデフォルトはfalse、uintのデフォルトは0である。 例えば、bool isActive = false
や uint total = 0
のように、変数宣言の際に開発者が明示的にデフォルト値を代入すると、よくある非効率が発生する。
これは機能的には正しいのですが、Solidityがデフォルトでこれらの値を設定しているため、デプロイ時に不要なガスコストが発生します。 ステート変数に値を代入せずに宣言することで、コントラクトのバイトコード・サイズを小さくし、余分なストレージ操作を避けることができる。 この小さな調整は、特に複数の変数を扱う場合に、スマート・コントラクトをより効率的で保守しやすくするのに役立つ。
次の例を考えてみよう:
チェーンデー タを最小限に
取引で発生するガス代の大半は、契約書保管庫に保存されたデータに由来することを十分に承知している。 実際にどのデータをオンチェーンまたはオフチェーンに保存する必要があるのかを常に問い、両方のオプションのトレードオフを検討するのがベストだ。 完全なオンチェーンNFTの場合、オフチェーンメタデータを使用した従来のNFTと比較して、いかに高価であるかがわかる。 つまり、情報をオフチェーンに保存することで、スマート・コントラクトのガス消費量を大幅に減らすことができる。
未使用ストレージの解放
時々、私たちは契約中の未使用データを解放することを忘れ、必ずガス代が高くなり、ネットワークの肥大化を引き起こす。 いずれにせよ、未使用のストレージの解放は、それがもう使用されないことを確認したら、値を0に戻すだけという簡単なものだ。 また、solidity の特別なキーワード delete
を使用して、任意のデータ型を解放することもできます。
次の例を考えてみよう: