使用 abi.encodePacked() 儲存字元串或將其轉換為 bytes32 是否更便宜?
例如,在下面的程式碼中,將 儲存
string
在 a 中會更便宜mapping
嗎?或者將其轉換為bytes32
並儲存為然後在我使用 JavaScript 呼叫它時bytes32
將其轉換回?string
mapping(string => bool) _tokenExists; function test(string memory _str) public { _tokenExists[_str] = true; }
或者
mapping(bytes32 => bool) _tokenExists; function test(string memory _str) public { _tokenExists[abi.encodePacked(_str)] = true; }
其中哪一種會更便宜、更好用,為什麼?
PS:有沒有一種方法可以讓我自己找出未來通過自己進行天然氣估算會更便宜的方法?
首先,
abi.encodePacked(_str)
將 轉換string
_str
為它的 UTF-8bytes
表示,而不是轉換為bytes32
.bytes
是一個動態大小的字節數組,就像string
. 由於Solidity 0.8.5可以bytes
轉換為bytes32
,但您需要注意,因為超過 32 字節的任何內容都將被截斷。因此,如果您
string
的 UTF-8bytes
表示為 32 字節或更短,則可以將其放在bytes32
viabytes32(abi.encodePacked(_str))
中,而如果您的字元串長於 32 字節,則可以將其壓縮為bytes32
viakeccak256(abi.encodePacked(_str))
。來自Solidity 文件:
對應於映射鍵的
k
值位於連接的位置,並且keccak256(h(k) . p)
是根據其類型應用於鍵的函式:.``h
- 對於值類型,
h
將值填充到 32 個字節,方法與將值儲存在記憶體中時的方式相同。- 對於字元串和字節數組,
h
計算keccak256
未填充數據的雜湊值。
p
上面是映射變數(你的_tokenExists
)的“槽位置”,所以在合約中它是恆定的。對於此範例,假設p = 0
. 所以我們有:
鑰匙類型 k
值槽位置 bytes32
keccak256( k . 0 )
bytes
或者string
keccak256( keccak256(abi.encodePacked(k)) . 0 )
因此,對於
mapping
帶有bytes
或string
鍵類型的 s,訪問一個mapping
值會呼叫一個額外的keccak256
。但是呼叫keccak256
足夠便宜,不會影響您的設計決策。相反,您應該使用string
或bytes32
取決於語義上更適合您的契約的內容。一個簡單的方法來測試上面的理論“你自己,未來”是通過在Remix IDE中載入一個測試合約,將它部署到
JavaScript VM
Environment
,並使用相同的輸入字元串呼叫不同的函式,嘗試短字元串和長字元串,並比較不同的功能’execution cost
,例如:pragma solidity ^0.8.5; contract Test { mapping(string => bool) _tokenExists1; function test1(string memory _str) external { _tokenExists1[_str] = true; } mapping(bytes => bool) _tokenExists2; function test2(string memory _str) external { _tokenExists2[abi.encodePacked(_str)] = true; } mapping(bytes32 => bool) _tokenExists3; function test3(string memory _str) external { _tokenExists3[keccak256(abi.encodePacked(_str))] = true; } mapping(bytes32 => bool) _tokenExists4; function test4(string memory _str) external { _tokenExists4[bytes32(abi.encodePacked(_str))] = true; } }