Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新(準備中)

Feb 14, 2022 - コメント - 日記

Solidityオプティマイザはストレージ変数の変更を避けてくれるのか?

English version

ブロックチェーン上のスマートコントラクトを開発するに当たって実行コストである「ガス代」を削減することは重要な事です。消費Gasを削減するためのいくつかの有名なトリック があるのですが、そのうちの一つに「ストレージ変数の変更を避ける(Avoid changing storage data)」というのがあります。

例えばUniswapV2のスマートコントラクトにはこんな感じでgas savingsのトリックが至る所に施されています。

Uniswap/v2-core

自分でスマートコントラクトを書いたり、レビューしたりするときにはこの点常に気をつけているのですが、先日ふと「これあまりにも古典的なトリックなので、すでにSolidityのオプティマイザで解決されてるのでは?」と思い、最新のSolidityコンパイラ0.8.11で検証してみました。

検証したスマートコントラクト

Hardhatプロジェクトを作成し、以下のようなスマートコントラクトを書きました。 検証ソースコード一式

counter1()はガス最適化していないコード、counter2()はガス最適化したコードです。違いはローカル変数(memory)jにStorage変数counterをコピーしてjでカウントした結果をcounterに戻しています。

テストコード

ガス測定のコードは以下のとおり

ガス代の測定

ガス代の測定のために便利なHardhatプラグインhardhat-gas-reporter を使います

インストールは以下のとおり

$ yarn add --dev hardhat-gas-reporter

としてhardhat.confg.jsの冒頭にrequire("hardhat-gas-reporter");を追加。

設定は、hardhat.confg.jsgasReporterを追加します。ここで環境変数CMC_API_KEYCoinmarketcap のAPIキーを設定するとUSD換算した費用がレポートされます。

module.exports = {
    ....
	gasReporter: {
		currency: "USD",
		gasPrice: 100,
		coinmarketcap: process.env.CMC_API_KEY,
	},
};

検証

準備ができたので早速検証していきます。まずSolidityのオプティマイザを利用せずに実行してみます。

$ npx hardhat test

Solidityオプティマイザオフ

結果は以下のようになりました

Solidityのオプティマイザ無し
コード最適化無し(counter1): 630,118
コード最適化有り(counter2): 407,289 (-25%)

25%ほどガスが削減できているようです、これはほぼ予想通りです。

次にSolidityのオプティマイザをオン(runs:1,000)にしてみます。

Solidityオプティマイザオン

結果は以下のようになりました

Solidityのオプティマイザ有り
コード最適化無し(counter1): 439,118
コード最適化有り(counter2): 218,271 (-50%)

Solidityのオプティマイザ有り/無しともに大きくガスの節約ができていますが、やはりコード最適化による効果は大きいようです。

結論

この「ストレージ変数の変更回数を避ける」というガス削減トリックは、最新のSolidityのオプティマイザを有効にしてもいまだに有効ということが分かりました。ストレージ変数の変更には引き続き注意を払いましょう。