はじめに

初めまして。 CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です! スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

代表的なゲームはクリプトスペルズというブロックチェーンゲームです。

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!

今回はSolidityのバージョン0.8.26が2024年5月21日にリリースされたので、その変更点をまとめていきます。

以下の公式のリリース記事をもとにまとめていきます。

変更点

Solidityのバージョン0.8.26では以下の項目が変更されました。

  • requireでのcustomエラーサポート
  • エンコードサイズが小さいカスタムエラーの最適化
  • 高速なYul Optimizerシーケンス
  • コンパイル時に使用されるJSONライブラリの変更

requireでのcustomエラーサポート

カスタムエラーとは、以下のように任意のエラーを自分で定義できる機能です。

solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

contract Solidity0826 {
    uint256 requiredAmount = 0.01 ether;

    error InsufficientBalance(uint256 available, uint256 required);

    function deposit() public payable {
        if (msg.value <= 0.001 ether) {
            revert InsufficientBalance(msg.value, requiredAmount);
        }
    }
}
solidity
error InsufficientBalance(uint256 available, uint256 required);

上記でInsufficientBalanceというエラーを定義しておきます。

solidity
if (msg.value <= 0.001 ether) {
    revert InsufficientBalance(msg.value, requiredAmount);
}

その後、上記のようにif文などの条件にマッチした場合に、先ほど定義したエラーを返すようにします。

エラーを返すときによく使用されるエラーとしては、requireというものがあります。 こちらの方がより一般的かつ、ガス効率が良いです。

solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

contract Solidity0826 {
    uint256 requiredAmount = 0.01 ether;

    error InsufficientBalance(uint256 available, uint256 required);

    function deposit() public payable {
        require(msg.value > 0.001 ether, "InsufficientBalance");
    }
}

上記のrequireでは、msg.value > 0.001 etherという条件に一致しない場合は、InsufficientBalanceというメッセージを返して処理を止めます。 しかし、Solidiry 0.8.25以前まではrequireのメッセージ部分にカスタムエラーを入れることができませんでした。

Solidiry 0.8.26では、ここにカスタムエラーを入れられるようになりました。

solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

contract Solidity0826 {
    uint256 requiredAmount = 0.01 ether;

    error InsufficientBalance(uint256 available, uint256 required);

    function deposit() public payable {
        require(
            msg.value > 0.001 ether ,
            InsufficientBalance(msg.value, requiredAmount)
        );
    }
}

これにより以前のカスタムエラーよりもガス効率が良くなります。

ただ、一点注意が必要です。 それは、コンパイル時にIRパイプライン(Yul経由でのコンパイル)でのみこの機能がサポートされているということです。 IRパイプラインとは、直接コードをバイトコードに変換するのではなく、中間表現(人間が読みやすいコードと機械語の中間に位置する表現形式)に変換して、より効率的なバイトコードを生成するコンパイル方式です。 SolidityではYulという中間表現があります。

Yulについては、以下の記事にを参考にしてください。

hardhatとremixでのIRパイプラインでのコンパイルをする方法を簡単に説明しておきます。

hardhat

hardhat.config.jsで以下のようにviaIR: true,を追加してください。

hardhat.config.js
solidity: {
  version: "0.8.24", // any version you want
  settings: {
+   viaIR: true,
    optimizer: {
      enabled: true,
      details: {
        yulDetails: {
          optimizerSteps: "u",
        },
      },
    },
  },
}

remix

remixでは、compiler_config.jsonに以下のように、"viaIR": true,を追加してください。

compiler_config.json

{
	"language": "Solidity",
	"settings": {
+		"viaIR": true,
		"optimizer": {
			"enabled": true,
			"runs": 200
		},
		"outputSelection": {
			"*": {
			"": ["ast"],
			"*": ["abi", "metadata", "devdoc", "userdoc", "storageLayout", "evm.legacyAssembly", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "evm.gasEstimates", "evm.assembly"]
			}
		}
	}
}

ソースコード

remixで作成したソースコードも載せておきます。

エンコードサイズが小さいカスタムエラーの最適化

これまでカスタムエラーを使用して処理をrevert(中断)する時のコードの最適化には、インラインアセンブリを使用していました。

インラインアセンプリについては、以下の記事にを参考にしてください。

Solidiry 0.8.26では、引数のないカスタムエラーやメモリ領域に収まる程度のサイズのカスタムエラーの場合、デプロイ段階でガスコストを自動で最適化してくれるようになります。 これにより、インラインアセンプリでカスタムエラーコードを最適化する必要がなくなります。

solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.26;

error ForceFailure();

contract FailureForcer {
    function fail() external pure {
        revert ForceFailure();
    }
}

引数のないカスタムエラーは上記のようなエラーです。

高速なYul Optimizerシーケンス

Yul Optimizerを使用してコンパイルするときのシーケンス(どのように最適化するか)に、新しいデフォルトシーケンスを導入することが決まりました。 新しいデフォルトシーケンスでは、コードの最適化の精度を維持しつつ、コンパイル時間を大幅に短縮しています。

これまでは最適化の手順を複数回繰り返して最適化の機会を増やしていましたが、解析の結果、最初の最適化と最終結果がほとんど変わらないことがわかりました。

そのため、新しいデフォルトシーケンスでは繰り返しの回数を減らして、1回の最適化が行われるように変更されました。

ガス代の効率化も同じように1回の最適化に変換したようです。

これにより、以下の図のように多くのプロジェクトでコンパイル時間が最大65%まで減少しました。

プロジェクト コンパイル時間の減少率 バイトコードサイズの変化 実行時ガスコストの変化
pool-together -63% -1.29% 未測定
uniswap -53% +1.67% 未測定
zeppelin -47% -0.48% -0.01%
elementfi -42% -1.87% 未測定
euler -34% +1.00% 未測定
yield_liquidator -27% +0.84% +0.14%
ens -22% -1.20% -0.01%
brink -20% +0.61% 未測定
perpetual-pools -16% -0.23% +0.02%
gp2 -12% +0.50% 未測定

注意点として、0.8.21以前のバージョンで問題となった脆弱性があり、新しいデフォルトシーケンスは0.8.22以降で使うようにして、それ以前のバージョンでは古いシーケンスを使用する必要があります。

コンパイル時に使用されるJSONライブラリの変更

コンパイル内部で使用されているJSONライブラリを、jsoncppというライブラリからnlohmann::jsonというライブラリに変更されました。

これによりUTF-8エンコーディング(可変長の文字エンコーディング方式)がより厳密になり、不正なUTF-8シーケンスが許容されなくなります。

また、速度も上がる?ようです。

その他の変更

その他の細かい修正については以下の記事を参考にしてください。

参考

最後に

今回はSolidityのバージョン0.8.26の変更点をまとめました。

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!