Solidity
為什麼與在 Solidity 中使用 calldata 相比,檢索儲存的數據成本如此之高?
我有一個函式,我需要將任意長度的地址數組傳遞給它。
address[]
在 remix 中,只需為 5 個地址的數組呼叫一個花費大約 30k gas 的函式:function fun(address[] calldata addresses) external returns (address[] memory) { return addresses; }
我要呼叫的地址是大約 2000 個地址的有限集合,但是當我呼叫我的函式時它可能是這些地址中的任何一個,所以我認為我可以將它們儲存在一個映射中
uint=>address
並使用數組呼叫我的函式uint16 的,然後我可以使用(我之前儲存的)查找地址。從理論上講,這應該便宜得多,因為呼叫數據要小得多:pragma solidity 0.5.17; contract StoreIDToAddress { mapping(uint => address) public numToAddress; function getAddresses(uint16[] calldata _IDs) external view returns (address[] memory) { address[] memory arr = new address[](_IDs.length); for (uint i; i < _IDs.length; i++) { arr[i] = numToAddress[_IDs[i]]; } return arr; } // Other functions to initialize numToAddress etc }
getAddresses
花費 29378 氣體,輸入為[0, 1, 2, 3, 4]
. 僅此一項就不會比僅在 calldata 中包含地址本身便宜,而且當我使用第二個合約進行測試時,該合約接收uint16[]
並傳遞給getAddresses
它需要 34196 gas:function testUint16Arr3(uint16[] calldata _arr) external returns (address[] memory) { return storeIDToAddress.getAddresses(_arr); }
使用
bytes
並將其解碼uint16
為呼叫getAddresses
是 35095 氣體。所以我的問題是,當(AFAIK)最大的操作是 5 x SLOAD = 1000 氣體時,為什麼要
getAddresses
花費這麼多氣體?如何降低將地址添加到我的函式中的 gas 成本?
伊斯坦布爾硬分叉包括EIP-1884,它(以及其他重新定價)將 SLOAD 從 200 重新定價到 800 天然氣。這意味著從儲存中載入 5 個地址將花費 4000 gas,不包括任何成本。伊斯坦布爾硬分叉還包括EIP-2028,它將非零呼叫數據字節的成本從 68 個降低到 16 個。這兩個 EIPS 是導致 gas 成本與您預期的巨大差異的原因。
正如@goodvibration 在評論中提到的那樣,所有交易都有 21000 gas 的內置成本。這是基本成本,您所做的任何契約互動都在此金額之上。