10

I read the documentation and tried some other approaches but it's not working for me. I'm not sure what I'm missing.

Let's say I have a contract with a mapping and I would like to know the storage slot location for a particular key and be able to access it directly from something like 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);
    }

}

The getStorageLocationForKey function is just an utility that I would like to use to derive this storage slot index from using Solidity.

Since the key is an address, I left padded the address to have a 32 bytes value:

0x000000000000000000000006827b8f6cc60497d9bf5210d602C0EcaFDF7C4050

Since the mapping, in this case, is declared at storage slot position 0, I padded it to 32 bytes:

0x0000000000000000000000000000000000000000000000000000000000000000

Concatenating them without the 0x prefix:

0000000000000000000000006827b8f6cc60497d9bf5210d602C0EcaFDF7C4050000000000000000000000000000000000000000000000000000000000000000

And its keccak256 hash:

6ccfcacc19c8d102cc5880b825323e63defac7b2ba969c7dc33950b298248835

You can try the keccak256 here: https://emn178.github.io/online-tools/keccak_256.html

I try:

web3.eth.getStorageAt(contractAddress, "0x6ccfcacc19c8d102cc5880b825323e63defac7b2ba969c7dc33950b298248835")

And it returns 0x bytes. I'm expecting to get the 678 value which is what it's saved at that address key.

I'm I missing something?

I would like to see a solution implemented in Solidity.

Here is an instance of this contract deployed in Rinkeby testnet:

0xC216FdC8fb87A151d69313e18a80a58bebBA7267

2 Answers 2

13

I managed to solve it myself:

// 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);
    }

}

I was doing a couple of things wrong. First, using the emn178.github.io site to calculate the keccak256 hash of the concatenation of the encoded and padded address and mapping storage index, I had the address checksummed, so, there were some uppercase characters and thus it was yielding the wrong hash. Also, the 'Input type' on that site was 'Text' by default, so I put it as 'Hex' so it knows how to treat my input while calculating the keccak256 hash.

I managed to implement it with Solidity, as I needed and it's working alright now.

Now calling the contract with the hash returns what I was expecting:

web3.eth.getStorageAt("0xC216FdC8fb87A151d69313e18a80a58bebBA7267", "0x86dfc0930cb222883cc0138873d68c1c9864fc2fe59d208c17f3484f489bef04")

Returns:

0x00000000000000000000000000000000000000000000000000000000000002a6

Which parsed to decimal is:

678

5

here you have an example using web3.js and soliditySha3

soliditySha3:

Will calculate the sha3 of given input parameters in the same way solidity would. This means arguments will be ABI converted and tightly packed before being hashed.

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));
}

and here you have an example with 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 provider.getStorageAt(
    "0xC216FdC8fb87A151d69313e18a80a58bebBA7267",
    hash
  );
  console.log("result ethers:", parseInt(result, 16));
}
1
  • Thank you Javier, that looks good. But I was looking for a way to implement that logic in Solidity also. But thanks for your effort. I managed to solve my issue. Commented Aug 12, 2022 at 3:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.