Solidity

如何計算映射鍵儲存中的位置索引(槽)

  • August 12, 2022

我閱讀了文件並嘗試了其他一些方法,但它對我不起作用。我不確定我錯過了什麼。

假設我有一個映射契約,我想知道特定密鑰的儲存槽位置,並能夠直接從以下位置訪問它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.jssoliditySha3的範例

堅固性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));
}

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