solidity-gas-optimization について
スマートコントラクトの開発者やレビュー担当者向けのSolidityガス最適化リファレンスです。影響度とリスクに基づいて8カテゴリの手法を整理し、安全でない手法や古いパターンにも注意を促します。
主な機能
- 018カテゴリにわたる最適化手法
- 02影響度とリスクによる優先順位
- 03ストレージ、デプロイ、calldataの手法
- 04コンパイラーとアセンブリのパターン
- 05危険または古い手法への警告
- 06ベンチマークと可読性の指針
活用シーン
- 01コントラクトのデプロイや実行コストの削減
- 02ガス削減の余地を探すコントラクトレビュー
- 03ストレージレイアウトや設計の比較
- 04低レベル最適化手法のリスク評価
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ pseudoyu/ agent-skills --skill solidity-gas-optimizationインストール後、Claude Code で/solidity-gas-optimizationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/solidity-gas-guidelines.mdreferences/rules/assembly-01-basics.mdcalldata-01-memory-vs-calldata.mdcompiler-01-loops.mdcompiler-02-math.mdcrosscall-01-multicall.mddeploy-01-custom-errors.mddeploy-02-clones-proxies.mddesign-01-token-standards.mddesign-02-signatures-vs-merkle.mdstorage-01-zero-to-one.mdstorage-02-cache-variables.mdstorage-03-packing.mdstorage-04-constants-immutables.md詳細
Solidity ガス最適化
概要
Solidity スマートコントラクト向けの包括的なガス最適化ガイドです。8 つのカテゴリにわたる 80 以上のテクニックを収録しています。RareSkills Book of Gas Optimization に基づいており、ルールは影響度と安全性に応じて優先順位付けされています。
適用するタイミング
次のような場合に、これらのガイドラインを参照してください。
- Solidity スマートコントラクトを新規作成するとき
- 既存のコントラクトをレビューまたは監査するとき
- デプロイまたは実行時のガスコストを最適化するとき
- コントラクトのストレージレイアウトをリファクタリングするとき
- コントラクト間のやり取りを実装するとき
- デザインパターン(ERC721 と ERC1155 など)を選択するとき
優先順位順のカテゴリ
| 優先順位 | カテゴリ | 影響度 | リスク |
|---|---|---|---|
| 1 | ストレージ最適化 | 重大 | 低 |
| 2 | デプロイ最適化 | 高 | 低 |
| 3 | Calldata 最適化 | 高 | 低 |
| 4 | デザインパターン | 高 | 中 |
| 5 | コントラクト間呼び出し | 中〜高 | 中 |
| 6 | コンパイラ最適化 | 中 | 低 |
| 7 | アセンブリのテクニック | 中 | 高 |
| 8 | 危険なテクニック | 低 | 重大 |
クイックリファレンス
重大:ストレージ最適化(最初に適用)
ゼロから非ゼロへの書き込み:
- ゼロから非ゼロへのストレージ書き込みは避けてください(22,100 gas がかかります)
- ブール値のような値には、0/1 ではなく 1/2 を使用してください
- ERC20 コントラクトでは最低残高を維持してください
変数のパッキング:
// Bad: 3 slots
struct Unpacked {
uint64 time; // slot 1
uint256 amount; // slot 2
address user; // slot 3
}
// Good: 2 slots
struct Packed {
uint64 time; // slot 1 (with address)
address user; // slot 1
uint256 amount; // slot 2
}
キャッシュ:
// Bad: reads storage twice
function increment() public {
require(count < 10);
count = count + 1;
}
// Good: reads storage once
function increment() public {
uint256 _count = count;
require(_count < 10);
count = _count + 1;
}
定数とイミュータブル:
uint256 constant MAX = 100; // No storage slot
address immutable owner; // Set in constructor, no storage
高:デプロイ最適化
カスタムエラー:
// Bad: ~64+ bytes
require(amount <= limit, "Amount exceeds limit");
// Good: ~4 bytes
error ExceedsLimit();
if (amount > limit) revert ExceedsLimit();
Payable コンストラクター:
// Saves ~200 gas on deployment
constructor() payable {}
クローンパターン:
- 繰り返しデプロイする場合は、EIP-1167 のミニマルプロキシを使用してください
- アップグレード可能なコントラクトには、Transparent Proxy より UUPS を使用してください
高:Calldata 最適化
Calldata と Memory:
// Bad: copies to memory
function process(bytes memory data) external {}
// Good: reads directly from calldata
function process(bytes calldata data) external {}
符号付き整数を避ける:
- 小さな負の数はコストが高くなります(例:-1 = 0xffff...)
- 関数パラメーターには符号なし整数を使用してください
高:デザインパターン
トークン規格:
- NFT には ERC721 より ERC1155 を優先してください(balanceOf のオーバーヘッドがありません)
- 複数の ERC20 を 1 つの ERC1155 に統合することを検討してください
署名と Merkle:
- Allowlist には Merkle ツリーより ECDSA 署名を優先してください
- 1 回の tx で approve と transfer を実行できるよう、ERC20Permit を実装してください
代替ライブラリ:
- ガス効率を高めるには、OpenZeppelin より Solmate/Solady の使用を検討してください
中〜高:コントラクト間呼び出し
やり取りを減らす:
- approve + transferFrom の代わりに、ERC1363 の transferAndCall を使用してください
- 操作をバッチ処理するための multicall を実装してください
- 外部呼び出しの結果をキャッシュしてください(例:Chainlink オラクル)
アクセスリスト:
- ストレージを事前にウォームアップするには、ERC2930 のアクセスリストトランザクションを使用してください
中:コンパイラ最適化
ループのパターン:
// Good: unchecked increment, cached length
uint256 len = arr.length;
for (uint256 i; i < len; ) {
// logic
unchecked { ++i; }
}
名前付き戻り値:
// More efficient bytecode
function calc(uint256 x) pure returns (uint256 result) {
result = x * 2;
}
ビットシフト:
// Cheaper: 3 gas
x << 1 // x * 2
x >> 2 // x / 4
// Expensive: 5 gas
x * 2
x / 4
短絡評価を使うブール式:
&&では、失敗する可能性が高い条件を先に置いてください||では、成功する可能性が高い条件を先に置いてください
中:アセンブリ(慎重に使用)
効率的なチェック:
// Check address(0) with assembly
assembly {
if iszero(caller()) { revert(0, 0) }
}
// Even/odd check
x & 1 // instead of x % 2
メモリの再利用:
- 小さな操作ではスクラッチ領域(0x00-0x40)を再利用してください
- ループ内でのメモリ拡張を避けてください
回避:危険なテクニック
これらは本番環境で安全ではありません。
- すべての関数を payable にする
- send() の戻り値を無視する
- 分岐に gasleft() を使用する
- テストで block.number を操作する
古いパターン
これらは最新の Solidity では適用されません。
- 「external は public より安価」— もはや正しくありません
- 「!= 0 は > 0 より安価」— 0.8.12 前後で変更されました
参考資料
コード例を含む完全なドキュメント:
references/solidity-gas-guidelines.md- 完全ガイドreferences/rules/- カテゴリ別の個別パターン
特定のパターンを調べるには:
grep -l "storage" references/rules/
grep -l "assembly" references/rules/
grep -l "struct" references/rules/
references/rules/ のルールカテゴリ
storage-*- ストレージ最適化パターンdeploy-*- デプロイ時のガス削減calldata-*- Calldata 最適化design-*- デザインパターンの選択crosscall-*- コントラクト間呼び出しの最適化compiler-*- コンパイラ最適化パターンassembly-*- 低レベルのアセンブリテクニック
主な原則
- 必ずベンチマークを実施する - コンパイラの挙動はコンテキストやバージョンによって異なります
- 読みやすさとのバランスを取る - すべての最適化がコードの複雑さに見合うとは限りません
- 両方の方法をテストする - 直感に反する最適化によって、かえってコストが増えることがあります
--via-irを検討する - 最新のコンパイラオプションにより、一部のテクニックが不要になる可能性があります- 代替ライブラリを使用する - Solmate/Soladyは、ガスコストの面でOpenZeppelinを上回ることがよくあります
コメント
Markdown 対応