Solidity 智能合約 Gas 優化技巧
如果一間區塊鏈公司業務,成本都來自於智能合約的交易,那麼智能合約的 gas 消耗,就直接影響公司營業成本。寫出節能的智能合約,在商用上有其重要性。這篇文章將整理分析智能合約中,影響 gas 消耗的一些因素,有助於我們了解消耗最大的指令是哪些,並謹慎的使用。gas 消耗可參考下面兩個表:
表一中的 Gas Used 為消耗的 gas。如果顯示為 FORMULA,表示不是一個固定值,要參考右邊的說明。由於我們通常使用 Solidity 開發,所以可能無法直接對應到上面表一中的指令;這時可參考表二,他有多加上一些額外的指令。以下列出消耗較多 gas 的行為與指令:
建立合約
對應到 CREATE
和 CODECOPY
兩個指令。在合約中建立另一個空的合約消耗 42901 gas ( 總共 64173 gas),如果是直接部署一個空合約則總共是 68653 gas。加上合約的功能實作,可能會有幾十萬甚至百萬的 gas,應該是所有指令中消耗最多一個。拆分多個類別與實例的實作方式,gas 消耗可能會很可觀。避免用 Contract 當作資料結構:
不好
1 | contract User { |
好
1 | contract MyContract { |
或
1 | contract MyContract { |
資料儲存
對應到 SSTORE
指令,新增資料要花 20000 gas,修改則花 5000。一個例外是將原本不為零的變數改為零,這個稍後討論。避免反覆修改,盡量最後再一次寫入:
不好
1 | uint256 public count; |
好
1 | for (uint256 i = 0; i < 10; ++i) { |
交易資料
傳送交易的基本 gas 為 21000,input 資料每個 byte 68 gas,如果 byte 為 0x00 則只算 4 gas。例如:0x0dbe671f,gas 為 68 * 4 = 272;如果是 0x0000001f 則為 68 * 1 + 4 * 3 = 80。由於所有的參數都是佔 32 bytes,所以當參數為零時,gas 消耗最小,為 32 * 4 = 128,而最大時如下公式:
1 | n: 參數的 byte 數 |
例如 bytes32
參數的 gas 消耗最大 2176,address
為 20 bytes 則是 1408。
轉帳
使用 call
、send
和 transfer
等函式,對應到 CALL
指令,基本消耗 7400 gas,加上其他消耗會將近 7600。值得注意的是如果轉帳到一個從未出現過的地址,會額外再多 25000 gas。
不會有額外消耗
1 | function withdraw(uint256 amount) { |
可能會有額外的消耗
1 | function withdrawTo(uint256 amount, address receiver) { |
ecrecover
對應到 CALL
指令,執行這個函式會消耗 3700 gas。
呼叫外部合約
呼叫外部合約會執行 EXTCODESIZE
和 CALL
指令,基本消耗 1400 gas。建議儘量不拆分合約實例,可用多重繼承來管理程式碼。
事件
對應到 LOG1
指令。一個沒有參數的事件基本 750 gas,每多一個參數理論會多 256 gas,不過實際會更多。
雜湊
智能合約內建了幾種雜湊函式可以使用,keccak256
、sha256
和 ripemd160
。參數越多,消耗的 gas 越多。gas 消耗 ripemd160
> sha256
> keccak256
,所以如果沒有其他目的,建議使用 keccak256
。
還有其他消耗較少但可以優化的部分,之後會再另外發一篇文章說明。怕有人不知道,最後提醒一下,編譯合約記得開啟優化選項。