62 Gas優化指南(2024)
在以太坊上執行智能合約時,Gas 是決定交易成本的關鍵因素。即使是微小的 Gas 差異,累積起來也可能對開發者與使用者的支出產生顯著影響。崔棉大師在「62 Gas優化」影片中提出了多項具體的技術方案,協助大家在鏈上互動時更有效率地管理成本。本文將以 列表式 的方式呈現核心要點,逐一說明每項技巧的原理與實作步驟,最後提供延伸閱讀資源,讓您在開發過程中能夠即時落實 Gas 優化。
核心要點列表
- 深入了解 Gas 計算模型
- 精簡資料結構與型別
- 降低儲存(Storage)操作次數
- 合理使用 calldata 與 memory
- 避免不必要的外部呼叫
- 使用可重入保護與檢查-效果-互動模式
- 在測試環境中預估 Gas 消耗
- 利用合約升級與代理模式降低重部署成本
以下將依序展開說明每一項要點,並提供具體的執行步驟。
1. 深入了解 Gas 計算模型
以太坊的 Gas 計算是根據每條 EVM 指令的「執行成本」來累計。掌握這個模型才能在編寫程式碼時有的放矢。
執行步驟
- 參考官方文件中的指令 Gas 表(
https://evm.codes)了解常見指令的成本。 - 在 Solidity 中使用
gasleft()觀測當前剩餘 Gas。 - 針對高成本指令(如
SSTORE、CALL)檢視是否有替代方案。
2. 精簡資料結構與型別
資料型別的選擇直接影響儲存與運算的 Gas 消耗。使用較小的型別或打包變數可以顯著降低成本。
執行步驟
- 優先使用
uint8、uint16等較小的整數型別,避免使用預設的uint256。 - 將多個小型變數打包在同一個 storage slot 中(例如
struct內的緊湊排列)。 - 使用
bytes或string時,盡量在 calldata 中傳遞,避免不必要的 memory 複製。
3. 降低儲存(Storage)操作次數
在 EVM 中,SSTORE 是最昂貴的指令之一。減少寫入次數是 Gas 優化的關鍵。
執行步驟
- 先在 memory 中完成所有計算,最後一次性寫回 storage。
- 若同一變數需要多次更新,先檢查新舊值是否相同,只有變更時才執行
SSTORE。 - 使用
mapping替代陣列,避免遍歷導致的多次寫入。
4. 合理使用 calldata 與 memory
calldata 為只讀且成本最低的資料區域,適合外部呼叫時傳遞參數;memory 則適用於需要臨時修改的情況。
執行步驟
- 在函式簽名中將大型陣列或字串宣告為
calldata(例如function foo(uint256[] calldata data))。 - 只在確實需要修改資料時,才將
calldata複製到memory。 - 減少不必要的
memory分配,避免在迴圈內重複建立。
5. 避免不必要的外部呼叫
每一次 CALL、DELEGATECALL 或 STATICCALL 都會產生額外的 Gas 費用。盡量在同一合約內完成邏輯,或使用內部函式呼叫。
執行步驟
- 檢視合約間的相依性,將可合併的功能抽離至同一合約。
- 使用
internal函式而非external,減少跨合約呼叫。 - 若必須呼叫外部合約,確保只傳遞必要的參數與資金。
6. 使用可重入保護與檢查-效果-互動模式
不當的外部呼叫可能導致重入攻擊,同時也會浪費 Gas。遵循「檢查-效果-互動」的設計模式,可同時提升安全與效率。
執行步驟
- 先檢查條件(如餘額是否足夠),再更新合約狀態,最後執行外部呼叫。
- 使用
ReentrancyGuard(OpenZeppelin)或自行實作鎖定機制。 - 確保外部呼叫的回傳值被正確處理,避免不必要的回滾。
7. 在測試環境中預估 Gas 消耗
在部署前先行測試 Gas 使用情況,可避免上線後的高額費用。
執行步驟
- 使用
hardhat、truffle或foundry等開發框架的estimateGas功能。 - 撰寫單元測試,針對每條重要路徑記錄 Gas 數值。
- 針對異常情況(如失敗回滾)也做 Gas 預估,確保安全上限。
8. 利用合約升級與代理模式降低重部署成本
頻繁的合約升級會產生額外的部署 Gas。透過代理(Proxy)模式,只需更新邏輯合約即可,節省大量 Gas。
執行步驟
- 采用
Transparent Proxy或UUPS Proxy(均可在OpenZeppelin找到範例)。 - 將不可變的資料(如管理者地址)放在 Proxy 本身,避免每次升級都重寫。
- 在升級前使用
upgradeTo估算 Gas,確保在可接受範圍內。
延伸閱讀
- 以太坊官方 Gas 手冊:
https://ethereum.org/zh/developers/docs/gas/ - OpenZeppelin 合約安全與升級指南:
https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable - Hardhat 官方文件的 Gas 預估功能:
https://hardhat.org/tutorial
常見問題
Q1: 為什麼 SSTORE 那麼貴,能否完全避免?
SSTORE 需要寫入區塊鏈的永久存儲,這是最耗費資源的操作。雖然無法完全避免,但可以透過前置計算、批次寫入或僅在值變更時才寫入的方式大幅降低次數。
Q2: 使用 calldata 與 memory 的差異會不會影響合約的功能?
calldata 為只讀且成本最低的參數傳遞方式,適合外部呼叫時的靜態資料。若需要在函式內部修改資料,則必須先複製到 memory。正確選擇可以兼顧功能與成本。
Q3: 在測試環境中估算的 Gas 與主網實際消費會有差異嗎?
測試環境的 Gas 計算與主網基本相同,但若使用不同的 EVM 實作或優化器設定,可能會有細微差異。建議在部署前於測試網(如 Goerli)進行實際交易,取得最接近的數據。
推薦交易平台
如果你正在尋找安全可靠的交易所: