Solidity

為什麼與在 Solidity 中使用 calldata 相比,檢索儲存的數據成本如此之高?

  • June 29, 2020

我有一個函式,我需要將任意長度的地址數組傳遞給它。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 的內置成本。這是基本成本,您所做的任何契約互動都在此金額之上。

引用自:https://ethereum.stackexchange.com/questions/84632