如何計算映射鍵儲存中的位置索引(槽)
我閱讀了文件並嘗試了其他一些方法,但它對我不起作用。我不確定我錯過了什麼。
假設我有一個映射契約,我想知道特定密鑰的儲存槽位置,並能夠直接從以下位置訪問它
web3.eth.getStorageAt(contractAddress, mappingKeyIndexInStorage)
:// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; contract MappingStorage { mapping(address => uint) public balances; constructor() { balances[0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405] = 678; balances[0x66B0b1d2930059407DcC30F1A2305435fc37315E] = 501; } function getStorageLocationForKey(address _key, uint256 p) public pure returns(bytes32) { // There is no straight foward way in solidity to concatenate 2 values, so I'm trying encoding here. bytes memory encoded = abi.encode(_key, p); return keccak256(encoded); } }
該
getStorageLocationForKey
函式只是一個實用程序,我想用它來通過使用 Solidity 派生這個儲存槽索引。由於鍵是地址,我將地址填充為 32 字節值:
0x0000000000000000000000006827b8f6cc60497d9bf5210d602C0EcaFDF7C4050
由於在這種情況下映射是在儲存槽位置聲明的
0
,因此我將其填充為 32 個字節:0x0000000000000000000000000000000000000000000000000000000000000000000
0x
在沒有前綴的情況下連接它們:00000000000000000000000006827b8f6cc60497d9bf5210d602C0EcaFDF7C40500000000000000000000000000000000000000000000000000000000000000000000
及其
keccak256
雜湊:6ccfcacc19c8d102cc5880b825323e63defac7b2ba969c7dc33950b298248835
你可以在
keccak256
這裡試試:https ://emn178.github.io/online-tools/keccak_256.html我嘗試:
web3.eth.getStorageAt(契約地址,“0x6ccfcacc19c8d102cc5880b825323e63defac7b2ba969c7dc33950b298248835”)
它返回
0x
字節。我期望獲得678
保存在該地址鍵中的值。我錯過了什麼?
我希望看到在 Solidity 中實現的解決方案。
這是部署在 Rinkeby 測試網中的該合約的一個實例:
0xC216FdC8fb87A151d69313e18a80a58bebBA7267
我設法自己解決了這個問題:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; contract MappingStorage { mapping(address => uint) public balances; uint8 constant balancesMappingIndex = 0; constructor() { balances[0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405] = 678; balances[0x66B0b1d2930059407DcC30F1A2305435fc37315E] = 501; } function getStorageLocationForKey(address _key) public pure returns(bytes32) { // This works pretty well as concatenation. For the address 0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405, // the storage slot index hash would be: 0x86dfc0930cb222883cc0138873d68c1c9864fc2fe59d208c17f3484f489bef04 return keccak256(abi.encode(_key, balancesMappingIndex)); } function getKeyEncodedWithMappingIndex(address _key) public pure returns(bytes memory) { // For the address 0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405, the encoded data would be: // 0x0000000000000000000000006827b8f6cc60497d9bf5210d602c0ecafdf7c4050000000000000000000000000000000000000000000000000000000000000000 return abi.encode(_key, balancesMappingIndex); } }
我做錯了幾件事。首先,使用emn178.github.io站點計算編碼和填充地址和映射儲存索引的連接的 keccak256 雜湊,我有地址校驗和,所以,有一些大寫字元,因此它產生了錯誤的雜湊.
keccak256
此外,該站點上的“輸入類型”預設為“文本”,因此我將其設置為“十六進制”,這樣它就知道在計算雜湊時如何處理我的輸入。我設法根據需要使用 Solidity 實現了它,現在它工作正常。
現在用雜湊呼叫合約返回我所期望的:
web3.eth.getStorageAt("0xC216FdC8fb87A151d69313e18a80a58bebBA7267", "0x86dfc0930cb222883cc0138873d68c1c9864fc2fe59d208c17f3484f489bef04")
回報:
0x0000000000000000000000000000000000000000000000000000000000000002a6
解析為十進制的是:
678
這裡有一個使用web3.js和soliditySha3的範例
堅固性Sha3:
將以與solidity相同的方式計算給定輸入參數的sha3。這意味著參數將在散列之前進行 ABI 轉換和緊密打包。
async function read() { const address= "0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405"; const slot = 0; const paddedAddress = web3.utils.leftPad(address, 64); const paddedSlot = web3.utils.padLeft(slot, 64); const hash = web3.utils.soliditySha3(paddedAddress, paddedSlot); const result = await web3.eth.getStorageAt( "0xC216FdC8fb87A151d69313e18a80a58bebBA7267", hash ); console.log("result:", parseInt(result, 16)); }
在這裡你有一個ethers.js的例子
async function read() { const address = "0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405"; const slot = 0; const paddedAddress = ethers.utils.hexZeroPad(address, 32); const paddedSlot = ethers.utils.hexZeroPad(slot, 32); const concatenated = ethers.utils.concat([paddedAddress, paddedSlot]); const hash = ethers.utils.keccak256(concatenated); const result = await web3.eth.getStorageAt( "0xC216FdC8fb87A151d69313e18a80a58bebBA7267", hash ); console.log("result ethers:", parseInt(result, 16)); }