# [EIP1087] ストレージ書き込みのガス節約の仕組みを理解しよう！

> EIP1087は、EVMにおけるSSTORE操作、つまりストレージへの書き込み処理にかかるガスコストの仕組みを見直す提案です。

- URL: https://cardene.dev/articles/6f2886262667837f7011
- 公開日: 2025-07-10
- 更新日: 2025-07-10
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

今回は、ストレージ変更に対する無駄なガス消費を削減し、より柔軟なスマートコントラクト設計を可能にするガス代の仕組みを提案している**EIP1087**についてまとめていきます！

:::message
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-1087

他にも様々なEIPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

## 概要

**EIP1087**は、EVMにおける`SSTORE`操作、つまりストレージへの書き込み処理にかかるガスコストの仕組みを見直す提案です。
現在の`SSTORE`の課金方式では、一時的な変更や結果的に元の状態に戻る操作にも多くのガスが消費されるため、これを合理化することが目的です。

**EIP1087**は、ストレージ操作に伴うガスコストが実際のリソース消費量と一致していないケースに対処することで、不要に高額なガス代を削減して新しいユースケースを可能にするための土台を提供します。

## 動機

現在、EVMにおける`SSTORE`（ストレージへの書き込み）操作は、以下のようなガス課金ルールに基づいています。

* ストレージスロットの値を　`0`　から `0` 以外に変更する。
    * `20,000` ガス
* その他の変更 `0` 以外から別の `0` 以外の値に変更、`0` 以外から `0` など）。
    * 5,000ガス
* スロットの値を `0` 以外から `0` に変更した場合。
    * 10,000ガスが後で返金（リファンド）される。

一見妥当な設定のように見えますが、複数の操作が1つのトランザクション中で行われる場合、実際にはディスクへの書き込みが発生していないにも関わらず、高額なガスが消費されてしまうという問題があります。

例えば、空のストレージを持つコントラクトがスロット0に「`1`」を書き込んだ後、すぐに「`0`」に戻す操作を行った場合、本質的にはストレージには何も変更が残らないにも関わらず、合計15,000ガス（20,000 + 5,000 - 10,000）が消費されます。

また、あるスロットの値を5回インクリメントする操作では、本質的に1回の書き込みと変わらないにも関わらず、45,000ガス（20,000 + 5×5,000）が必要になります。

さらに、アドレスAからアドレスBへ送金し、続けてアドレスBからアドレスCへ送金するような一連の操作でも、各アカウントの残高は変化しているものの、ディスクの書き込みに対して課金が過剰となっています。

このような非効率な課金体系を見直すことにより、以下のような新しいユースケースが現実的になります。

例えば、reentrancy（再入）攻撃を防ぐためのミューテックス（ロック機構）のように、一時的なフラグを書き込むがトランザクション終了時には元に戻るような処理。
また、複数の関数呼び出し間でコンテキスト情報を受け渡すようなロジックなどです。

その一例として「`approveAndCall`」という操作が挙げられます。
これはERC20の`approve`と`transfer`を一つのトランザクションでまとめて行うような機能で、トークン規格の変更を伴わずに柔軟なインターフェースを実現できるため、コントラクトの利便性を大きく高めることができます。

こうした新しい使い方は、現在のガスコスト体系ではコストが高く、実用的ではないケースが多いのですが、**EIP1087**によってそれらが現実的に利用可能になると期待されます。

## 仕様

**EIP1087**では、EVMがストレージへの書き込みに対して課すガスコストの計算方法を見直し、複数回の書き換えが行われる場合のガス消費を大幅に削減する仕組みが導入されます。
そのために、各トランザクションごとに「**ダーティマップ（dirty map）**」という仕組みが使われます。

### ダーティマップとは何か？

ダーティマップは、トランザクション中に変更されたストレージスロットの一覧を記録するマップです。
EVMはトランザクションの実行中、各コントラクトのどのストレージスロットが変更されたかを記録し、その情報をもとにガス課金とリファンド（返金）を計算します。

注意すべき点は、トランザクション中の内部呼び出しでロールバック（`revert`）が発生した場合、その呼び出しの中で更新されたダーティマップの内容も巻き戻される点です。
これはストレージの変更と同様のスコープを持ちます。

### ストレージ書き込みに対するガス課金

ストレージへの書き込み操作における新たなガス課金ルールは以下のとおりです。

まず、現在の値と同じ値を書き込んだ場合（no-op）は、単純に200ガスが差し引かれます。

次に、そのスロットが今回のトランザクションで初めて変更された場合、以下のように処理されます。

* 元の値が0だった場合は、20,000ガスを消費。
* 元の値が0でない場合は、5,000ガスを消費。

その後、そのスロットは「**ダーティ**」としてマークされます。
すでに「**ダーティ**」となっているスロットに対して再度書き込みを行った場合は、200ガスのみが差し引かれます（ディスクへの追加コストが発生しないため）。

### トランザクション終了時のガス返金（リファンド）

トランザクションの終了時、ダーティマップに記録された各ストレージスロットについて、以下の条件に応じてガスが返金されます。

* 元の値が0で現在も0の場合は、19,800ガスが返金される。
* 元の値が0以外で値が変わっていない場合は、4,800ガスが返金される。
* 元の値が0以外で現在は0になっている場合は、15,000ガスが返金される。

このリファンドの仕組みにより、「**一時的な書き換えで結果として元に戻る**」ような操作に対しては、実際のリソース使用に見合った少ないガスのみが課金されるようになります。

### 具体的なコスト削減例

以下は、従来のガス課金方式と比較したときの実際のガス消費量の差です。

* 空のストレージに対し、スロット0を1に設定した後すぐに0に戻す操作
    * 従来の場合は、15,000ガス。
    * 新方式では、400ガス（20,000 + 200 - 19,800）。

* 空のストレージでスロット0を5回インクリメントする操作
    * 従来の場合は、45,000ガス。
    * 新方式の場合は、21,000ガス（20,000 + 5×200）。

* 口座A→B→Cの連続送金（すべて0以外の残高）
    * 従来の場合は、20,000ガス。
    * 新方式の場合は、10,400ガス（5,000×3 + 200 - 4,800）。

### この仕様変更の意義

この仕様変更により、1つのストレージスロットをトランザクション中に何度も更新するようなコントラクトロジックに対して、より合理的なガス課金が行われるようになります。
これにより、これまでコストがかかりすぎて実用化できなかった高度なコントラクト設計が現実的な選択肢となります。

特に、オンチェーンでの状態の一時的な記録や再入防止のミューテックス処理、複雑な状態遷移を伴うコントラクトにおいて、大きな恩恵が期待されます。

## 互換性

**EIP1087**を実装するためにはハードフォークが必要です。
ハードフォークとは、Ethereumの全ノードが新しいルールセットに切り替える必要がある、互換性を伴わないプロトコル変更を意味します。

ただし、**EIP1087**は既存のスマートコントラクトの動作を壊すことはなく、むしろ多くの場合でガスコストを削減する効果があるため、スマートコントラクトの層（コントラクトレイヤー）における互換性の問題は発生しないと見込まれています。
つまり、既存のコントラクトはこの変更によって動作が変わることなく、ガス代だけが安くなる可能性が高いということです。

## テストケース

**EIP1087**による新しいガス課金・返金ロジックを検証するためのテストケースがいくつか定義されています。
これらのケースでは、ストレージスロットの元の値と新しい値の組み合わせ、またはトランザクションの挙動（revertなど）に応じたガス使用量と返金の挙動が確認されます。

以下、代表的なテストです。

- ストレージが0の状態で、新しい0以外の値を書き込むと20,000ガスが消費され、返金はありません。
- スロットに既に入っている0以外の値を、異なる0以外の値に変更した場合は、5,000ガスが消費されて返金はされません。
- 0以外の値が入ったスロットを0に書き換えると、5,000ガスが消費されて10,000ガスが後で返金されます（従来のリファンドルールと整合）。
- すでに0であるスロットに0を再び書き込むと、200ガスのみが消費され返金はされません（変更がないため、最小限のコストに）。
- すでに入っている値と同じ値を再度書き込んでも、200ガスがかかり返金はされません。
- スロットが0のときにxを書き込み、その後異なるyをさらに書き込むと、20,000 + 200 = 20,200ガスがかかり返金はされません。
    - 初回変更が高コスト、2回目は安価です。
- 同様に、x → y → zのように複数の異なる値を連続で書き換える場合も、ガスは5,200程度に抑えられて返金はされません。
- スロットが0の状態でxを書き込み、最終的に0に戻すような場合は、20,200ガスかかりますが、19,800ガスが返金されて実質のコストはわずかです。
- もともとyが入っていたスロットにxを書き、その後再びyに戻す場合は、5,200ガスがかかり4,800ガスが返金されます。
- ストレージ操作がrevertされた場合、ガスが消費されて返金はされません。
    - これは、ロールバックされた書き込みはダーティマップに残らないためです。
- ネストされたコール（内部呼び出し）でxを書き、その後戻って0を書くようなパターンでも、返金は正しく適用され19,800ガスが戻ってきます。

これらのテストケースにより、新しいガス課金・返金ロジックが正しく適用され、無駄なガスの発生を防ぎつつ、既存の動作を壊さないことが確認できます。
これにより、Ethereum上のコントラクトはより効率的に動作するようになり、開発者にとっても予測可能かつ有利なガスコスト設計が可能になります。

## 引用

Nick Johnson (@arachnid), "EIP-1087: Net gas metering for SSTORE operations [DRAFT]," Ethereum Improvement Proposals, no. 1087, May 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1087.

## 最後に

今回は「一時的なストレージ変更に対する無駄なガス消費を削減し、より柔軟なスマートコントラクト設計を可能にするガス代の仕組みを提案している**EIP1087**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
