---
id: 24
title: "Gas 機制精讀：計量單位、費用市場與執行中止"
slug: 0-7-gas-mechanics
date: 2026/09/25
summary: Gas 是 EVM 的資源計量單位，價格則由費用市場決定；把兩者混為一談，會誤判擴容與執行中止的邊界。下面拆解固有成本與動態成本的構成，說明 out-of-gas 與 REVERT 各退什麼，EIP-1559 的 base fee 與 tip 各歸誰。
keywords: Gas計量,EIP-1559,固有成本,out-of-gas,EIP-2929
heroImage: /images/articles/photos/0-7-gas-mechanics.jpg
---

一筆最簡的 ETH 轉帳固定消耗 21000 gas（黃皮書附錄 G 的 Gtransaction）；一筆 ERC-20 轉帳的消耗取決於合約實現與槽位冷熱狀態，通常落在數萬 gas 這個量級，這是量級估計而非實測值。這些數字由協議規定，只有硬分叉才會改動；而同樣一筆交易實際支付多少 ETH，可以在幾小時內變化十倍以上。前者是計量單位，後者是價格。把這兩個概念分開，費用討論、擴容敘事與執行中止的行為才談得清楚。

Gas 機制同時承擔三件事：把計算與狀態操作折算成統一單位，用一個價格市場分配有限的區塊空間，並在資源耗盡時給出確定的回退邊界。下面按這三條線展開：固有成本與動態成本各包含什麼，冷熱存取如何計價，交易在無效、REVERT 與 out-of-gas 三種中止下分別退回什麼，EIP-1559 之後 base fee、priority fee 與執行層 gas 的邊界在哪裡，以及為什麼 Gas 讓 DoS 變得可計價，卻無法讓定價永遠正確。

## Gas 是計算與儲存的計量單位，與幣價是兩回事

Gas 是抽象的計價單位，對應執行一條指令、讀寫一次狀態、擴充一個記憶體字所消耗的抽象資源量。它的數值由協議常數給定，例如加法 3 gas（Gverylow）、柏林升級（區塊 12,244,000，2021 年 4 月 15 日）之後 SLOAD 熱存取 100 gas、SSTORE 從零寫非零 20000 gas。這些常數只有透過硬分叉才會變化，與市場供需無關。

Gas 的價格以 wei 計價，常用單位是 gwei，1 gwei 等於 10⁹ wei。一筆交易的費用等於 gas_used 乘以 effective_gas_price，前者由執行過程決定，後者由費用市場與交易簽章裡的出價共同決定。這個乘法關係是後面全部討論的基礎。

分清這兩層有幾個直接好處。討論「降 gas」時，先確認目標到底是降低 gas_used 還是降低 gas price：前者要靠減少操作、最佳化儲存布局或修改 EVM 計價規則，後者取決於當下需求與費用市場的形態，EIP-1559 讓後者更可預測，但沒有改變任何一條指令的價格。討論「手續費歸誰」時，base fee 部分被協議銷毀，不存在收款方，只有 priority fee 進入驗證者帳戶。討論「交易上限」時，gas_limit 是容量上限而非費用上限，費用上限是 max_fee_per_gas，兩者是不同的欄位。

## 固有成本：執行開始前就已扣掉的部分

每筆交易要先付固有成本（intrinsic gas），它在 EVM 執行第一條指令之前就被扣掉。黃皮書式 (64) 把它記為 g₀：21000 的固定底價（Gtransaction），加上資料欄位每個零位元組 4 gas、每個非零位元組 16 gas（非零位元組價由 EIP-2028 在伊斯坦堡升級，區塊 9,069,000，2019 年 12 月 8 日，從 68 降到 16），若為合約建立交易再加 32000（Gtxcreate）；EIP-2930（柏林升級，區塊 12,244,000，2021 年 4 月 15 日）之後，access list 中的每個位址 2400 gas、每個儲存鍵 1900 gas；EIP-3860（上海升級，區塊 17,034,870，2023 年 4 月 12 日）之後，合約建立的 initcode 每 32 位元組字再收 2 gas。

21000 的含義容易被誤讀成「轉帳的手續費」。準確的說法是，它表示「有一筆交易」的底價，與轉帳金額無關。最簡 ETH 轉帳的 calldata 為空，資料部分不產生額外成本，所以正好是 21000。

EIP-2929（柏林升級，區塊 12,244,000，2021 年 4 月 15 日）還規定了交易開始時的熱集合：發送方位址、接收方位址（建立交易則是被建立的位址）以及所有預編譯合約位址已經在 accessed_addresses 中，對它們不收取冷存取費用。這是為什麼一筆轉帳調向普通位址時，接收方的帳戶存取不會額外收費。

固有成本在執行前扣除，因此有三條行為邊界。gas_limit 低於固有成本的交易直接無效，不會被包含進區塊，也不扣任何 gas。執行中途因 out-of-gas 中止時，固有成本不會退回。access list 的 2400 與 1900（柏林之後的價格）屬於預付費：走訪一遍將要存取的位址與槽位，把它們提前置為熱，但如果實際執行沒有存取，這筆錢不退。

## 動態成本：記憶體擴充與冷熱存取如何累加

執行階段的成本由靜態部分與動態部分相加。靜態部分是操作碼的固定價，黃皮書附錄 G 的費率表用幾個檔位表示：Gverylow 3 gas（ADD、SUB、MLOAD、MSTORE、PUSH 系列等）、Glow 5、Gmid 8、Ghigh 10、Gbase 2、Gjumpdest 1。

動態部分取決於運算元規模與狀態存取冷熱。記憶體擴充按 C_mem(a) = 3a + ⌊a² / 512⌋ 計價（黃皮書式 (328)，Gmemory = 3），a 是字數，實際扣費為擴充前後之差，這項在 704 位元組（22 個字）以內近似線性，之後二次項開始生效。複製類操作在基礎價上按字數加收 3 gas/字（Gcopy）；KECCAK256 收 30 gas（Gkeccak256）加 6 gas/字（Gkeccak256word）；LOG 收 375 gas（Glog）加每個 topic 375 gas（Glogtopic），再加 8 gas/位元組（Glogdata）；EXP 收 10 gas（Gexp）加 50 gas/指數位元組（Gexpbyte）。

冷熱存取由 EIP-2929（柏林升級）定義。首次存取某個 (位址, 槽位) 對是冷存取，SLOAD 收 2100 gas；同一交易內再次存取是熱存取，收 100 gas。帳戶類存取的冷價是 2600 gas，覆蓋 CALL、CALLCODE、DELEGATECALL、STATICCALL、BALANCE 與 EXTCODESIZE、EXTCODECOPY、EXTCODEHASH。這套集合是交易作用域的，作用域回退時集合一併回退。

| 操作（柏林升級之後口徑） | 冷存取 | 熱存取 |
|--------------------------|--------|--------|
| SLOAD | 2100 | 100 |
| CALL 系列、BALANCE、EXT* | 2600 | 100 |
| SSTORE 的附加冷存取費 | 2100 | 不加收 |

冷熱機制的直接來源是兩次 DoS 攻擊留下的教訓。EIP-150（Tangerine Whistle，區塊 2,463,000，2016 年 10 月 18 日）在 2016 年 9 月與 10 月的攻擊後，已經把 SLOAD 從 50 gas 抬到 200、CALL 從 40 抬到 700、SELFDESTRUCT 從 0 抬到 5000。EIP-2929 的動機引用了 2019 年論文 arXiv:1909.07220 中的一次測量：在作者那套硬體上重放以太坊歷史時，同一批惡意交易要跑 20 到 80 秒，而普通交易只需幾毫秒，說明狀態讀取類操作碼仍然定價過低。這是一次性測量，只代表 2019 年的硬體與負載，不能當作持續追蹤的口徑。

## 執行中止的三種結局：無效交易、REVERT 與 out-of-gas

交易可能在三個不同階段結束，回退範圍與費用處理各不相同。

第一種是無效交易，包括 nonce 不匹配、簽章錯誤、餘額不足以覆蓋 gas 預付款、gas_limit 低於固有成本等情況。這類交易根本不會進入區塊，不消耗 gas；它屬於鏈下拒絕，與執行中止是兩回事。

第二種是 REVERT，由 revert 語句、require 失敗或內嵌組譯的 revert 觸發。當前框架的狀態變更全部回退，剩餘 gas 退回給上一層，已經消耗的部分照常支付；退款計數器也回退到進入該框架之前。REVERT 是「有理由的失敗」，可以把錯誤資料回傳，適合需要讓呼叫方區分失敗原因的場景。

第三種是異常中止（exceptional halt），包括 out-of-gas、非法操作碼、堆疊溢位、靜態呼叫中寫狀態、initcode 超限等。狀態同樣回退，但當前框架剩餘的 gas 全部銷毀。若異常發生在頂層框架，交易的 gas_used 等於 gas_limit，費用按滿額扣除，沒有退回的部分。這就是「OOG 之後 gas 全部消耗」的確切含義：當前框架的剩餘 gas 歸零，與整條鏈的餘額無關。

```solidity
// REVERT：狀態回退，剩餘 gas 退回，已消耗的照付
function guarded(uint256 x) external pure returns (uint256) {
    require(x != 0, "zero");
    return 1e18 / x;
}

// 異常中止：無限迴圈觸發 out-of-gas，當前框架剩餘 gas 全部銷毀
function oog() external pure {
    while (true) {}
}
```

子呼叫的失敗是否牽連父框架，由 EIP-150（Tangerine Whistle）引入的「保留 1/64」規則決定。呼叫子框架時，父框架最多只能轉發自己剩餘 gas 的 63/64（即 gas - gas // 64），至少保留 1/64；CREATE 與 CREATE2 同樣只提供 63/64。因此子框架 OOG 不會連帶父框架 OOG，父框架可以用保留的 gas 捕捉失敗並繼續執行。這條規則在 2016 年替換了原先的呼叫深度限制，把「深度炸彈」從一類結構性攻擊降級為純 gas 問題。

```solidity
// 最多向子呼叫轉發 gasleft() 的 63/64，父框架保留 1/64
(bool ok, ) = target.call{gas: gasleft()}("");
```

帶 value 的 CALL 還有兩個附加項（黃皮書附錄 G）：非零轉帳收 Gcallvalue 9000 gas，若目標帳戶不存在再收 Gnewaccount 25000 gas；被呼叫方會得到 2300 gas 的呼叫補貼（Gcallstipend），用於最簡單的接收邏輯。EIP-2200（伊斯坦堡升級，區塊 9,069,000，2019 年 12 月 8 日）規定 gasleft 小於等於 2300 時 SSTORE 直接以 OOG 失敗，正是為了防止這段補貼路徑被用來寫入狀態。

退款（refund）在交易執行結束後結算，執行期間不可見。EIP-3529（倫敦升級，區塊 12,965,000，2021 年 8 月 5 日）把非零改寫為零的退款從 15000 降到 4800，取消了 SELFDESTRUCT 退款，並把單筆交易的總退款上限壓到 gas_used // 5。由於退款在執行期間不可用，合約不能依賴它來墊付交易中途需要的 gas。

## EIP-1559：base fee、tip 與執行層的邊界

EIP-1559（倫敦升級，區塊 12,965,000，2021 年 8 月 5 日）把交易費用拆成兩段。base fee 由協議按父區塊的 gas_used 與目標值（gas_limit 的一半）計算，單塊最多上下調整 12.5%，且被銷毀。priority fee（tip）是使用者給驗證者的額外單價，用於爭取更早打包。交易簽章裡給出 max_fee_per_gas 與 max_priority_fee_per_gas 兩個上限，實際生效單價是：priority fee 取 max_priority_fee_per_gas 與 (max_fee_per_gas - base fee) 的較小值，effective_gas_price 等於 priority fee 加 base fee。未使用的 gas 按這個單價退回。

執行層的邊界也要說清。GASPRICE 操作碼在 EIP-1559 之後回傳的是 effective_gas_price，也就是發送方實付單價；驗證者實得的部分無法從執行環境裡直接讀到。這也意味著任何依賴 GASPRICE 判斷「礦工收入」的舊邏輯都不再成立。

兩段費用回答的是不同問題：base fee 決定進入區塊的最低門檻，tip 決定同一區塊內的優先順序。區塊空間總量沒有變化，當持續用量超過目標值時，base fee 會逐塊上升把需求擠出去，這是排隊機制，與產能擴張無關。把 EIP-1559 當作降低 gas_used 的方案是常見誤讀，它只改變價格形成方式。

還有一層容易混淆的邊界：blob 費用不屬於執行層 gas。EIP-4844（坎昆升級，區塊 19,426,587，2024 年 3 月 13 日）為 blob 資料引入了獨立的基礎費與計價單位（blob gas），EIP-7516 增加了回傳 blob base fee 的操作碼。Rollup 的資料發布成本討論的是 blob 市場，與合約執行消耗的 gas 是兩套帳本。

## DoS 的「可計價」不等於定價正確

Gas 的核心安全屬性是將資源佔用折算成成本。要讓一個區塊被垃圾交易佔滿，攻擊者必須支付與該區塊全部 gas 對應的費用，這是「可計價」的含義。相比不收費的網路，攻擊的邊際成本從零變成與佔用成正比。

但這只在定價與真實成本相符時才有效。2016 年 9 月與 10 月的 DoS 攻擊利用的正是被低估的狀態讀取與呼叫類操作碼，直接催生了 Tangerine Whistle 硬分叉；此後 EIP-1884（伊斯坦堡升級，區塊 9,069,000，2019 年 12 月 8 日）把 SLOAD 從 200 抬到 800，BALANCE 與 EXTCODEHASH 從 400 抬到 700，EIP-2929 又把狀態存取拆成冷熱兩檔並大幅提高冷存取價，理由仍是同樣的模式可以把單塊處理時間拉到幾十秒。EIP-3860 管的是 initcode 的跳轉目標分析，此前這段工作量完全沒有計量。計價規則與實現成本之間存在結構性落後：客戶端的資料庫布局與執行引擎持續最佳化，gas 常數卻要等下一次硬分叉才會重估。

退款機制提供了反向的例證。它本來鼓勵合約清理不再使用的狀態，實際卻催生了 GasToken 這類把狀態槽位當電池、在低費率時囤積 gas、在高費率時釋放的方案，造成狀態膨脹與區塊 gas 使用量的額外變異數。EIP-3529 削減與封頂退款，就是為了關掉這條通道。

機制失效的條件有幾處需要單獨指出。定價是治理產物，常數通常按各實現的最壞用例挑選（EIP-3860 就寫明其 2 gas/字來自不同實現的最壞情況基準），而不同客戶端在同一批操作上的耗時不同，同一 gas 價格對應的真實資源成本因此並不一致。這是一個定性判斷；EIP-2929 為抬價給出的定量證據是 2019 年在某套測試硬體上的一次性測量，不能當作持續口徑。排序市場的價值並不完全由 tip 承載，MEV 拍賣與客戶端策略都會影響打包順序，tip 高只是更可能進入下一塊的激勵之一。區塊 gas 上限本身就是吞吐量與驗證成本的權衡，抬高上限會同時抬高全節點的硬體門檻。

這一節的落點在於說明它是一套需要定期校準的機制：「gas 定價可靠」與「gas 定價不可靠」都是過強的結論。EIP-150、EIP-1884、EIP-2929、EIP-3860 都是校準動作。至於下一次校準由什麼觸發，只能給推斷而不是結論：四次校準的直接誘因都是被公開觀測到的資源放大攻擊，據此推斷下一次大概率仍是同類事件。這個推斷沒有公開依據，也不排除因狀態增長、區塊容量等其他考量提前重估。

## 邊界與不確定的地方

引用的口徑是黃皮書附錄 G 費率表與式 (64)、式 (328)（下載版本的頁腳標註為 Shanghai 版本），以及 EIP-150、EIP-1884、EIP-2028、EIP-2200、EIP-1559、EIP-2929、EIP-2930、EIP-3529、EIP-3860 的規範文本。具體數字隨分叉變化，正文標出的分叉與區塊高度是：伊斯坦堡（2019 年 12 月 8 日，區塊 9,069,000）、柏林（2021 年 4 月 15 日，區塊 12,244,000）、倫敦（2021 年 8 月 5 日，區塊 12,965,000）、上海（2023 年 4 月 12 日，區塊 17,034,870）、坎昆（2024 年 3 月 13 日，區塊 19,426,587）。在 EVM 相容鏈上討論 gas 之前，需要先確認該鏈啟用了哪些分叉，計價常數可能與以太坊主網不同。Tangerine Whistle 的區塊 2,463,000 與 2016 年 10 月 18 日同樣來自 ethereum.org 的升級歷史。

關於「定價落後」的嚴重程度，只能給出旁證，給不出量化結論：公開材料裡沒有一份持續追蹤「協議 gas 常數與實際執行耗時比值」的時間序列，因此無法判斷落後是在擴大還是收斂。這一點應作為不確定項保留，不用它反推「正在惡化」或「已經收斂」之類的結論。

另外，費用市場的行為受 MEV 與客戶端策略影響，沒有展開排序市場的機制細節，也沒有把 blob 費用市場與執行層 gas 放在同一框架裡比較，兩者在 EIP-4844 之後是分開的計價體系。引用這些數字做成本模型時，需要先確認口徑與版本。

## 資料來源

- EIP-1559: Fee market change for ETH 1.0 chain，https://eips.ethereum.org/EIPS/eip-1559
- EIP-2929: Gas cost increases for state access opcodes，https://eips.ethereum.org/EIPS/eip-2929
- EIP-3529: Reduction in refunds，https://eips.ethereum.org/EIPS/eip-3529
- EIP-150: Gas cost changes for IO-heavy operations，https://eips.ethereum.org/EIPS/eip-150
- EIP-1884: Repricing for trie-size-dependent opcodes，https://eips.ethereum.org/EIPS/eip-1884
- EIP-2028: Transaction data gas cost reduction，https://eips.ethereum.org/EIPS/eip-2028
- EIP-2200: Structured Definitions for Net Gas Metering，https://eips.ethereum.org/EIPS/eip-2200
- EIP-2930: Optional access lists，https://eips.ethereum.org/EIPS/eip-2930
- EIP-3860: Limit and meter initcode，https://eips.ethereum.org/EIPS/eip-3860
- EIP-4844: Shard Blob Transactions，https://eips.ethereum.org/EIPS/eip-4844
- EIP-7516: BLOBBASEFEE opcode，https://eips.ethereum.org/EIPS/eip-7516
- Ethereum Yellow Paper，附錄 G 費率表、式 (64) 固有成本、式 (328) 記憶體計價函式，https://ethereum.github.io/yellowpaper/paper.pdf
- ethereum.org 開發者文件：Gas 與費用，https://ethereum.org/en/developers/docs/gas/
- ethereum.org 操作碼參考，https://ethereum.org/en/developers/docs/evm/opcodes/
- ethereum.org 網路升級歷史（各分叉區塊高度與日期），https://ethereum.org/en/history/
- arXiv:1909.07220，EIP-2929 引用的狀態存取耗時測量，https://arxiv.org/abs/1909.07220

## 延伸閱讀

- [《效能指標詞典：TPS、BPS、確認延遲、最終性、衝突率》](/zh-Hant/blog/performance-metrics-glossary)
- [《區塊鏈擴容地圖：L1 平行、L2、分片與 DA 各自解決什麼》](/zh-Hant/blog/blockchain-scaling-map)
- [《去中心化與效能的張力：驗證者門檻、硬體與地理分布》](/zh-Hant/blog/decentralization-performance-tradeoff)

本篇屬於 EVM 基礎系列，前置篇目為 0.5《三類儲存別搞混：Memory、Storage 與 Transient Storage》。
