如果一間區塊鏈公司業務,成本都來自於智能合約的交易,那麼智能合約的 gas 消耗,就直接影響公司營業成本。寫出節能的智能合約,在商用上有其重要性。這篇文章將整理分析智能合約中,影響 gas 消耗的一些因素,有助於我們了解消耗最大的指令是哪些,並謹慎的使用。gas 消耗可參考下面兩個表:

表一中的 Gas Used 為消耗的 gas。如果顯示為 FORMULA,表示不是一個固定值,要參考右邊的說明。由於我們通常使用 Solidity 開發,所以可能無法直接對應到上面表一中的指令;這時可參考表二,他有多加上一些額外的指令。以下列出消耗較多 gas 的行為與指令:

建立合約

對應到 CREATECODECOPY 兩個指令。在合約中建立另一個空的合約消耗 42901 gas ( 總共 64173 gas),如果是直接部署一個空合約則總共是 68653 gas。加上合約的功能實作,可能會有幾十萬甚至百萬的 gas,應該是所有指令中消耗最多一個。拆分多個類別與實例的實作方式,gas 消耗可能會很可觀。避免用 Contract 當作資料結構:

不好

1
2
3
4
5
6
7
8
9
contract User {
uint256 public amount;
bool public isAdmin;

function User(uint256 _amount, bool _isAdmin) {
amount = _amount;
isAdmin = _isAdmin;
}
}

1
2
3
4
contract MyContract {
mapping(address => uint256) amount;
mapping(address => bool) isAdmin;
}

1
2
3
4
5
6
7
8
contract MyContract {
struct User {
uint256 amount;
bool isAdmin;
}

mapping(address => User) users;
}

資料儲存

對應到 SSTORE 指令,新增資料要花 20000 gas,修改則花 5000。一個例外是將原本不為零的變數改為零,這個稍後討論。避免反覆修改,盡量最後再一次寫入:

不好

1
2
3
4
5
6
uint256 public count;
// ...
for (uint256 i = 0; i < 10; ++i) {
// ...
++count;
}

1
2
3
4
for (uint256 i = 0; i < 10; ++i) {
// ...
}
count += 10;

交易資料

傳送交易的基本 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
2
n: 參數的 byte 數
n * 68 + ( 32 - n ) * 4

例如 bytes32 參數的 gas 消耗最大 2176,address 為 20 bytes 則是 1408。

轉帳

使用 callsendtransfer 等函式,對應到 CALL 指令,基本消耗 7400 gas,加上其他消耗會將近 7600。值得注意的是如果轉帳到一個從未出現過的地址,會額外再多 25000 gas。
不會有額外消耗

1
2
3
function withdraw(uint256 amount) {
msg.sender.transfer(amount);
}

可能會有額外的消耗

1
2
3
function withdrawTo(uint256 amount, address receiver) {
receiver.transfer(amount);
}

ecrecover

對應到 CALL 指令,執行這個函式會消耗 3700 gas。

呼叫外部合約

呼叫外部合約會執行 EXTCODESIZECALL 指令,基本消耗 1400 gas。建議儘量不拆分合約實例,可用多重繼承來管理程式碼。

事件

對應到 LOG1 指令。一個沒有參數的事件基本 750 gas,每多一個參數理論會多 256 gas,不過實際會更多。

雜湊

智能合約內建了幾種雜湊函式可以使用,keccak256sha256ripemd160。參數越多,消耗的 gas 越多。gas 消耗 ripemd160 > sha256 > keccak256,所以如果沒有其他目的,建議使用 keccak256

還有其他消耗較少但可以優化的部分,之後會再另外發一篇文章說明。怕有人不知道,最後提醒一下,編譯合約記得開啟優化選項。

延伸閱讀

下一篇 Solidity 智能合約 Gas 優化技巧 - 函式名稱