# イーサリアムスマートコントラクトGas最適化ベストプラクティスイーサリアム主網のGas費用は常に厄介な問題であり、特にネットワークが混雑している時に顕著です。ピーク時にはユーザーは高額な取引手数料を支払う必要があります。そのため、スマートコントラクト開発段階でのGas費用の最適化が重要です。Gas消費の最適化は、取引コストを効果的に削減するだけでなく、取引効率を向上させ、ユーザーにとってより経済的で効率的なブロックチェーン体験をもたらします。この記事では、イーサリアム仮想マシン(EVM)のGas費メカニズム、Gas費最適化のコアコンセプト、およびスマートコントラクト開発時のGas費最適化のベストプラクティスについて概説します。これらの内容が開発者にインスピレーションと実用的な支援を提供し、一般ユーザーがEVMのGas費用の運用方式をよりよく理解し、ブロックチェーンエコシステムの課題に共に取り組む助けになることを願っています。## EVMのGas料金メカニズムの概要EVM互換ネットワークでは、「Gas」は特定の操作を実行するために必要な計算能力を測定する単位です。EVMの構造レイアウトでは、Gasの消費は3つの部分に分かれています: 操作の実行、外部メッセージの呼び出し、およびメモリとストレージの読み書き。各取引の実行には計算リソースが必要なため、無限ループやサービス拒否(DoS)攻撃を防ぐために一定の料金がかかります。取引を完了するために必要な料金は「ガス料金」と呼ばれます。EIP-1559( ロンドンハードフォーク) の発効以来、Gas費は以下の式で計算されています:ガス料金 = 使用されたガスの単位 * (基準料金 + 優先料金)基本料金は破棄され、優先料金はインセンティブとして機能し、バリデーターが取引をブロックチェーンに追加することを促します。取引を送信する際に優先料金を高く設定することで、その取引が次のブロックに含まれる可能性が高まります。これは、ユーザーがバリデーターに支払う「チップ」のようなものです。! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス](https://img-cdn.gateio.im/social/moments-187da99010b8fe26c21280bf193d1373)### EVM でのガス最適化の理解Solidityでスマートコントラクトをコンパイルすると、コントラクトは一連の"操作コード"、つまりopcodesに変換されます。任意の操作コード(、例えば契約の作成、メッセージの呼び出し、アカウントストレージへのアクセス、および仮想マシン上での操作の実行)には、認知されたGas消費コストが存在し、これらのコストはイーサリアムの黄皮書に記録されています。複数回のEIPの修正を経て、一部のオペコードのGasコストが調整されており、黄皮書にあるものとは異なる可能性があります。### ガス最適化の基本概念Gasの最適化の核心理念は、EVMブロックチェーン上でコスト効率の高い操作を優先的に選択し、Gasコストが高い操作を避けることです。EVMにおいて、以下の操作はコストが低い:- メモリ変数の読み書き- 定数と不変変数の読み取り - ローカル変数の読み書き- calldata 配列や構造体などの calldata 変数を読み取る- 内部関数呼び出しコストが高い操作には:- コントラクトストレージに保存されている状態変数の読み書き- 外部関数呼び出し- ループ操作! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス](https://img-cdn.gateio.im/social/moments-b237228ebe933741fb60f2e8bcb384050192837465674839201## EVMガス料金最適化ベストプラクティス上記の基本概念に基づいて、私たちは開発者コミュニティのためにGas料金最適化のベストプラクティスリストを整理しました。これらのプラクティスに従うことで、開発者はスマートコントラクトのGas料金消費を削減し、取引コストを低下させ、より効率的でユーザーフレンドリーなアプリケーションを構築できます。) 1.ストレージの使用をできるだけ減らすSolidityにおいて、Storage###は限られたリソースであり、そのGas消費はMemory(のそれを大幅に上回ります。スマートコントラクトがストレージからデータを読み書きするたびに、高額なGasコストが発生します。イーサリアムの黄皮書の定義によれば、ストレージ操作のコストはメモリ操作の100倍以上高いです。例えば、OPcodesmloadとmstore命令はわずか3ガス単位を消費しますが、ストレージ操作であるsloadとsstoreは、最も理想的な状況でもコストが少なくとも100単位必要です。制限ストレージ使用の方法には、- 非永続データをメモリに保存する- ストレージの変更回数を減らす: 中間結果をメモリに保存し、すべての計算が完了した後に結果をストレージ変数に割り当てる。! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/social/moments-30f0bc370a7b9ca65f3d623c31262b76() 2.変数パッケージングスマートコントラクト中使用されるStorage slot(ストレージスロット)の数および開発者がデータを表現する方法は、Gas費の消費に大きな影響を与える。Solidityコンパイラは、コンパイルプロセス中に連続したストレージ変数をパッケージ化し、32バイトのストレージスロットを変数ストレージの基本単位として使用します。変数のパッケージ化とは、変数を合理的に配置することで、複数の変数が単一のストレージスロットに適合できるようにすることを指します。この詳細な調整により、開発者は20,000ガス単位###を節約できます。未使用のストレージスロットを保存するには20,000ガス(が必要ですが、現在はわずか2つのストレージスロットのみで済みます。各ストレージスロットはGasを消費するため、変数のパッケージ化は必要なストレージスロットの数を減らすことでGasの使用を最適化します。! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/social/moments-995905cb414526d4d991899d0c2e64430192837465674839201( 3.データ型の最適化変数は様々なデータタイプで表すことができますが、異なるデータタイプに対応する操作コストも異なります。適切なデータタイプを選択することで、Gasの使用を最適化するのに役立ちます。例えば、Solidityでは、整数は異なるサイズに細分化できます:uint8、uint16、uint32など。EVMが256ビット単位で操作を実行するため、uint8を使用すると、EVMはまずそれをuint256に変換する必要があり、この変換は追加のガスを消費します。単独で見ると、uint256を使用する方がuint8よりも安価です。しかし、以前提案した変数パッキングの最適化を使用する場合は異なります。開発者が4つのuint8変数を1つのストレージスロットにパッキングできれば、それらを反復処理する総コストは4つのuint256変数よりも低くなります。こうすることで、スマートコントラクトは1回のストレージスロットの読み書きを行い、1回の操作で4つのuint8変数をメモリ/ストレージに配置することができます。! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/social/moments-55fcdb765912ef9cd238c46b1d248cff() 4.固定サイズの変数を動的変数の代わりに使用するデータが32バイト以内に制御できる場合は、bytesまたはstringsの代わりにbytes32データ型を使用することをお勧めします。一般的に、固定サイズの変数は可変サイズの変数よりもガスを少なく消費します。バイトの長さを制限できる場合は、できるだけbytes1からbytes32の最小の長さを選択してください。### 5.マッピングと配列Solidityのデータリストは2つのデータ型で表現できます: 配列(Arrays)とマッピング###Mappings###ですが、それらの構文と構造は全く異なります。マッピングはほとんどの場合、効率が高くコストが低いですが、配列はイテラブルでデータ型のバンドルをサポートしています。したがって、データリストを管理する際には、イテレーションが必要ない限り、またはデータ型のバンドルによってガス消費を最適化できる場合を除いて、マッピングを優先して使用することをお勧めします。! [イーサリアムスマートコントラクトのガス最適化のためのトップ10ベストプラクティス](https://img-cdn.gateio.im/social/moments-5f3d7e103e47c886f50599cffe35c707)( 6. メモリの代わりに calldata を使用する関数の引数で宣言された変数は、calldataまたはmemoryに格納できます。両者の主な違いは、memoryは関数によって変更可能ですが、calldataは不変であることです。この原則を覚えておいてください: 関数の引数が読み取り専用である場合、memoryではなくcalldataを優先して使用すべきです。これにより、関数のcalldataからmemoryへの不要なコピー操作を回避できます。) 7.できるだけConstant/Immutableキーワードを使用してくださいConstant/Immutable変数は契約のストレージに保存されません。これらの変数はコンパイル時に計算され、契約のバイトコードに保存されます。したがって、ストレージに比べてアクセスコストははるかに低くなるため、可能な限りConstantまたはImmutableキーワードを使用することをお勧めします。### 8.オーバーフロー/アンダーフローが発生しないことを確認してからUncheckedを使用する開発者が算術操作がオーバーフローやアンダーフローを引き起こさないことを確認できる場合、Solidity v0.8.0で導入されたuncheckedキーワードを使用して、余分なオーバーフローやアンダーフローのチェックを回避し、Gasコストを節約できます。さらに、0.8.0以上のバージョンのコンパイラでは、SafeMathライブラリを使用する必要がなくなりました。なぜなら、コンパイラ自体にオーバーフローおよびアンダーフロー保護機能が組み込まれているからです。### 9.オプティマイザ修正器のコードは修正された関数に組み込まれ、修正器を使用するたびにそのコードがコピーされます。これによりバイトコードのサイズが増加し、ガス消費が増加します。内部関数_checkOwner()にロジックを再構築することで、修飾子内でこの内部関数を再利用でき、バイトコードのサイズを削減し、ガスコストを低減できます。![イーサリアムスマートコントラクトのGas最適化トップ10ベストプラクティス]###https://img-cdn.gateio.im/social/moments-c0701f9e09280a1667495d54e262dd2f###( 10.ショートサーキット最適化||および&&演算子に対して、論理演算はショートサーキット評価が行われます。すなわち、最初の条件が論理式の結果を決定できる場合、2番目の条件は評価されません。ガス消費を最適化するために、計算コストが低い条件を前に置くべきです。そうすることで、コストの高い計算をスキップする可能性があります。! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/social/moments-a823fb7761aafa6529a6c45304e0314b(## その他の一般的な推奨事項) 1.無駄なコードを削除する契約に未使用の関数や変数が存在する場合は、それを削除することをお勧めします。これは契約のデプロイコストを削減し、契約のサイズを小さく保つ最も直接的な方法です。以下は幾つかの実用的なアドバイスです:最も効率的なアルゴリズムを使用して計算を行います。契約内で特定の計算結果を直接使用する場合、冗長な計算プロセスは削除すべきです。本質的に、使用されていない計算はすべて削除されるべきです。イーサリアムでは、開発者はストレージスペースを解放することでGas報酬を得ることができます。不要になった変数は、deleteキーワードを使用して削除するか、デフォルト値に設定するべきです。ループ最適化: 高コストのループ操作を避け、可能な限りループを統合し、繰り返し計算をループ本体の外に移動させる。![イーサリアムスマートコントラクトのGas最適化トップ10ベストプラクティス]###https://img-cdn.gateio.im/social/moments-839b91e2f02389949aa698d460a497d8() 2.プレコンパイルコントラクトの使用プレコンパイルコントラクトは、暗号化やハッシュ操作などの複雑なライブラリ関数を提供します。コードはEVM上で実行されるのではなく、クライアントノードのローカルで実行されるため、必要なGasが少なくなります。プレコンパイルコントラクトを使用することで、スマートコントラクトの実行に必要な計算作業量を削減し、Gasを節約することができます。プレコンパイルコントラクトの例には、楕円曲線デジタル署名アルゴリズム###ECDSA(とSHA2-256ハッシュアルゴリズムが含まれます。スマートコントラクト内でこれらのプレコンパイルコントラクトを使用することで、開発者はガスコストを削減し、アプリケーションの実行効率を向上させることができます。) 3.インラインアセンブリコードを使用するインラインアセンブリ###in-line assembly(は、開発者がEVMによって直接実行可能な低レベルかつ効率的なコードを記述できるようにし、高価なSolidityオペコードを使用する必要をなくします。インラインアセンブリは、メモリとストレージの使用をより正確に制御できることを可能にし、Gas費用をさらに削減します。加えて、インラインアセンブリはSolidityのみでは実現が難しい複雑な操作を実行でき、Gas消費の最適化により多くの柔軟性を提供します。しかし、インラインアセンブリの使用はリスクを伴い、間違いやすいです。そのため、経験豊富な開発者に限って慎重に使用すべきです。! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/social/moments-a141884dcdcdc56faff12eee2601b7b7###( 4.
イーサリアムスマートコントラクトGas最適化の詳細:10の実用的なテクニック
イーサリアムスマートコントラクトGas最適化ベストプラクティス
イーサリアム主網のGas費用は常に厄介な問題であり、特にネットワークが混雑している時に顕著です。ピーク時にはユーザーは高額な取引手数料を支払う必要があります。そのため、スマートコントラクト開発段階でのGas費用の最適化が重要です。Gas消費の最適化は、取引コストを効果的に削減するだけでなく、取引効率を向上させ、ユーザーにとってより経済的で効率的なブロックチェーン体験をもたらします。
この記事では、イーサリアム仮想マシン(EVM)のGas費メカニズム、Gas費最適化のコアコンセプト、およびスマートコントラクト開発時のGas費最適化のベストプラクティスについて概説します。これらの内容が開発者にインスピレーションと実用的な支援を提供し、一般ユーザーがEVMのGas費用の運用方式をよりよく理解し、ブロックチェーンエコシステムの課題に共に取り組む助けになることを願っています。
EVMのGas料金メカニズムの概要
EVM互換ネットワークでは、「Gas」は特定の操作を実行するために必要な計算能力を測定する単位です。
EVMの構造レイアウトでは、Gasの消費は3つの部分に分かれています: 操作の実行、外部メッセージの呼び出し、およびメモリとストレージの読み書き。
各取引の実行には計算リソースが必要なため、無限ループやサービス拒否(DoS)攻撃を防ぐために一定の料金がかかります。取引を完了するために必要な料金は「ガス料金」と呼ばれます。
EIP-1559( ロンドンハードフォーク) の発効以来、Gas費は以下の式で計算されています:
ガス料金 = 使用されたガスの単位 * (基準料金 + 優先料金)
基本料金は破棄され、優先料金はインセンティブとして機能し、バリデーターが取引をブロックチェーンに追加することを促します。取引を送信する際に優先料金を高く設定することで、その取引が次のブロックに含まれる可能性が高まります。これは、ユーザーがバリデーターに支払う「チップ」のようなものです。
! イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス
EVM でのガス最適化の理解
Solidityでスマートコントラクトをコンパイルすると、コントラクトは一連の"操作コード"、つまりopcodesに変換されます。
任意の操作コード(、例えば契約の作成、メッセージの呼び出し、アカウントストレージへのアクセス、および仮想マシン上での操作の実行)には、認知されたGas消費コストが存在し、これらのコストはイーサリアムの黄皮書に記録されています。
複数回のEIPの修正を経て、一部のオペコードのGasコストが調整されており、黄皮書にあるものとは異なる可能性があります。
ガス最適化の基本概念
Gasの最適化の核心理念は、EVMブロックチェーン上でコスト効率の高い操作を優先的に選択し、Gasコストが高い操作を避けることです。
EVMにおいて、以下の操作はコストが低い:
コストが高い操作には:
! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス](https://img-cdn.gateio.im/webp-social/moments-b237228ebe933741fb60f2e8bcb38405.webp0192837465674839201
EVMガス料金最適化ベストプラクティス
上記の基本概念に基づいて、私たちは開発者コミュニティのためにGas料金最適化のベストプラクティスリストを整理しました。これらのプラクティスに従うことで、開発者はスマートコントラクトのGas料金消費を削減し、取引コストを低下させ、より効率的でユーザーフレンドリーなアプリケーションを構築できます。
) 1.ストレージの使用をできるだけ減らす
Solidityにおいて、Storage###は限られたリソースであり、そのGas消費はMemory(のそれを大幅に上回ります。スマートコントラクトがストレージからデータを読み書きするたびに、高額なGasコストが発生します。
イーサリアムの黄皮書の定義によれば、ストレージ操作のコストはメモリ操作の100倍以上高いです。例えば、OPcodesmloadとmstore命令はわずか3ガス単位を消費しますが、ストレージ操作であるsloadとsstoreは、最も理想的な状況でもコストが少なくとも100単位必要です。
制限ストレージ使用の方法には、
! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/webp-social/moments-30f0bc370a7b9ca65f3d623c31262b76.webp(
) 2.変数パッケージング
スマートコントラクト中使用されるStorage slot(ストレージスロット)の数および開発者がデータを表現する方法は、Gas費の消費に大きな影響を与える。
Solidityコンパイラは、コンパイルプロセス中に連続したストレージ変数をパッケージ化し、32バイトのストレージスロットを変数ストレージの基本単位として使用します。変数のパッケージ化とは、変数を合理的に配置することで、複数の変数が単一のストレージスロットに適合できるようにすることを指します。
この詳細な調整により、開発者は20,000ガス単位###を節約できます。未使用のストレージスロットを保存するには20,000ガス(が必要ですが、現在はわずか2つのストレージスロットのみで済みます。
各ストレージスロットはGasを消費するため、変数のパッケージ化は必要なストレージスロットの数を減らすことでGasの使用を最適化します。
! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/webp-social/moments-995905cb414526d4d991899d0c2e6443.webp0192837465674839201
( 3.データ型の最適化
変数は様々なデータタイプで表すことができますが、異なるデータタイプに対応する操作コストも異なります。適切なデータタイプを選択することで、Gasの使用を最適化するのに役立ちます。
例えば、Solidityでは、整数は異なるサイズに細分化できます:uint8、uint16、uint32など。EVMが256ビット単位で操作を実行するため、uint8を使用すると、EVMはまずそれをuint256に変換する必要があり、この変換は追加のガスを消費します。
単独で見ると、uint256を使用する方がuint8よりも安価です。しかし、以前提案した変数パッキングの最適化を使用する場合は異なります。開発者が4つのuint8変数を1つのストレージスロットにパッキングできれば、それらを反復処理する総コストは4つのuint256変数よりも低くなります。こうすることで、スマートコントラクトは1回のストレージスロットの読み書きを行い、1回の操作で4つのuint8変数をメモリ/ストレージに配置することができます。
! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/webp-social/moments-55fcdb765912ef9cd238c46b1d248cff.webp(
) 4.固定サイズの変数を動的変数の代わりに使用する
データが32バイト以内に制御できる場合は、bytesまたはstringsの代わりにbytes32データ型を使用することをお勧めします。一般的に、固定サイズの変数は可変サイズの変数よりもガスを少なく消費します。バイトの長さを制限できる場合は、できるだけbytes1からbytes32の最小の長さを選択してください。
5.マッピングと配列
Solidityのデータリストは2つのデータ型で表現できます: 配列(Arrays)とマッピング###Mappings###ですが、それらの構文と構造は全く異なります。
マッピングはほとんどの場合、効率が高くコストが低いですが、配列はイテラブルでデータ型のバンドルをサポートしています。したがって、データリストを管理する際には、イテレーションが必要ない限り、またはデータ型のバンドルによってガス消費を最適化できる場合を除いて、マッピングを優先して使用することをお勧めします。
! イーサリアムスマートコントラクトのガス最適化のためのトップ10ベストプラクティス
( 6. メモリの代わりに calldata を使用する
関数の引数で宣言された変数は、calldataまたはmemoryに格納できます。両者の主な違いは、memoryは関数によって変更可能ですが、calldataは不変であることです。
この原則を覚えておいてください: 関数の引数が読み取り専用である場合、memoryではなくcalldataを優先して使用すべきです。これにより、関数のcalldataからmemoryへの不要なコピー操作を回避できます。
) 7.できるだけConstant/Immutableキーワードを使用してください
Constant/Immutable変数は契約のストレージに保存されません。これらの変数はコンパイル時に計算され、契約のバイトコードに保存されます。したがって、ストレージに比べてアクセスコストははるかに低くなるため、可能な限りConstantまたはImmutableキーワードを使用することをお勧めします。
8.オーバーフロー/アンダーフローが発生しないことを確認してからUncheckedを使用する
開発者が算術操作がオーバーフローやアンダーフローを引き起こさないことを確認できる場合、Solidity v0.8.0で導入されたuncheckedキーワードを使用して、余分なオーバーフローやアンダーフローのチェックを回避し、Gasコストを節約できます。
さらに、0.8.0以上のバージョンのコンパイラでは、SafeMathライブラリを使用する必要がなくなりました。なぜなら、コンパイラ自体にオーバーフローおよびアンダーフロー保護機能が組み込まれているからです。
9.オプティマイザ
修正器のコードは修正された関数に組み込まれ、修正器を使用するたびにそのコードがコピーされます。これによりバイトコードのサイズが増加し、ガス消費が増加します。
内部関数_checkOwner()にロジックを再構築することで、修飾子内でこの内部関数を再利用でき、バイトコードのサイズを削減し、ガスコストを低減できます。
![イーサリアムスマートコントラクトのGas最適化トップ10ベストプラクティス]###https://img-cdn.gateio.im/webp-social/moments-c0701f9e09280a1667495d54e262dd2f.webp###
( 10.ショートサーキット最適化
||および&&演算子に対して、論理演算はショートサーキット評価が行われます。すなわち、最初の条件が論理式の結果を決定できる場合、2番目の条件は評価されません。
ガス消費を最適化するために、計算コストが低い条件を前に置くべきです。そうすることで、コストの高い計算をスキップする可能性があります。
! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/webp-social/moments-a823fb7761aafa6529a6c45304e0314b.webp(
その他の一般的な推奨事項
) 1.無駄なコードを削除する
契約に未使用の関数や変数が存在する場合は、それを削除することをお勧めします。これは契約のデプロイコストを削減し、契約のサイズを小さく保つ最も直接的な方法です。
以下は幾つかの実用的なアドバイスです:
最も効率的なアルゴリズムを使用して計算を行います。契約内で特定の計算結果を直接使用する場合、冗長な計算プロセスは削除すべきです。本質的に、使用されていない計算はすべて削除されるべきです。
イーサリアムでは、開発者はストレージスペースを解放することでGas報酬を得ることができます。不要になった変数は、deleteキーワードを使用して削除するか、デフォルト値に設定するべきです。
ループ最適化: 高コストのループ操作を避け、可能な限りループを統合し、繰り返し計算をループ本体の外に移動させる。
![イーサリアムスマートコントラクトのGas最適化トップ10ベストプラクティス]###https://img-cdn.gateio.im/webp-social/moments-839b91e2f02389949aa698d460a497d8.webp(
) 2.プレコンパイルコントラクトの使用
プレコンパイルコントラクトは、暗号化やハッシュ操作などの複雑なライブラリ関数を提供します。コードはEVM上で実行されるのではなく、クライアントノードのローカルで実行されるため、必要なGasが少なくなります。プレコンパイルコントラクトを使用することで、スマートコントラクトの実行に必要な計算作業量を削減し、Gasを節約することができます。
プレコンパイルコントラクトの例には、楕円曲線デジタル署名アルゴリズム###ECDSA(とSHA2-256ハッシュアルゴリズムが含まれます。スマートコントラクト内でこれらのプレコンパイルコントラクトを使用することで、開発者はガスコストを削減し、アプリケーションの実行効率を向上させることができます。
) 3.インラインアセンブリコードを使用する
インラインアセンブリ###in-line assembly(は、開発者がEVMによって直接実行可能な低レベルかつ効率的なコードを記述できるようにし、高価なSolidityオペコードを使用する必要をなくします。インラインアセンブリは、メモリとストレージの使用をより正確に制御できることを可能にし、Gas費用をさらに削減します。加えて、インラインアセンブリはSolidityのみでは実現が難しい複雑な操作を実行でき、Gas消費の最適化により多くの柔軟性を提供します。
しかし、インラインアセンブリの使用はリスクを伴い、間違いやすいです。そのため、経験豊富な開発者に限って慎重に使用すべきです。
! [イーサリアムスマートコントラクトのガス最適化のためのトップ10のベストプラクティス])https://img-cdn.gateio.im/webp-social/moments-a141884dcdcdc56faff12eee2601b7b7.webp###
( 4.