PatrickAlphaC / hardhat-fund-me-fcc

82 stars 184 forks source link

[Solidity] Retrieving slots and values for Arrays and Mappings #5

Closed abrel closed 2 years ago

abrel commented 2 years ago

Hi,

First of all many thanks for this great course on hardhat & solidity. I am following lesson 7: Hardhat fund me but I cannot figure how to find slots and value for mappings & arrays cf : comments from deploy script

0. Solidity Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FunWithStorage {
    uint256 favoriteNumber; // Stored at slot 0
    bool someBool; // Stored at slot 1
    uint256[] myArray; /* Array Length Stored at slot 2,
    but the objects will be the keccak256(2), since 2 is the storage slot of the array */
    mapping(uint256 => bool) myMap; /* An empty slot is held at slot 3
    and the elements will be stored at keccak256(h(k) . p)
    p: The storage slot (aka, 3)
    k: The key in hex
    h: Some function based on the type. For uint256, it just pads the hex
    */
    uint256 constant NOT_IN_STORAGE = 123;
    uint256 immutable i_not_in_storage;

    constructor() {
        favoriteNumber = 25; // See stored spot above // SSTORE
        someBool = true; // See stored spot above // SSTORE
        myArray.push(222); // SSTORE
        myMap[0] = true; // SSTORE
        i_not_in_storage = 123;
    }

    function doStuff() public {
        uint256 newVar = favoriteNumber + 1; // SLOAD
        bool otherVar = someBool; // SLOAD
        // ^^ memory variables
    }
}

1. Array stuff

To retrieve slots for uint256[] myArray (position = 2 ; cf: Solidity code)

I am using the following code :

  const arrayPositionInStore = 2;
  const arraySlot = ethers.utils.keccak256(ethers.utils.hexlify(arrayPositionInStore));
  const arrayValue = await ethers.provider.getStorageAt(
    funWithStorage.address,
    ethers.utils.keccak256(ethers.utils.hexlify(2)),
  );

However arrayValue is :

'0x0000000000000000000000000000000000000000000000000000000000000000'

While I am expecting 222 here.

2. Mapping stuff

To retrieve slots for uint256[] myArray (position = 3 ; cf: Solidity code)

I am using the following code :

  const mappingPositionInStore = 3;
  const key = 0;
  const mapKeyInStore = ethers.utils.keccak256(
    ethers.utils.concat([
      ethers.utils.hexlify(mappingPositionInStore),
      ethers.utils.hexlify(key),
    ]),
  );

  const mappingValue = await ethers.provider.getStorageAt(
    funWithStorage.address,
    mapKeyInStore,
  );

However mappingValue is :

'0x0000000000000000000000000000000000000000000000000000000000000000'

while I am expecting 1 (true).

Could you please give me some pointers regarding what I am doing wrong ?

PatrickAlphaC commented 2 years ago

Wonderfully formatted issue!

So a few things:

Array Stuff

  1. The arrayPositionInStore is 2, however, in order to use keccak256, we need to pass it the unpacked version of 2 (yes, I also think ethers should be smart enough to convert it correctly!)

Running:

console.log(ethers.utils.hexlify(2))

Will return:

0x02

But you want:

0x0000000000000000000000000000000000000000000000000000000000000002

You could use something like hexZeroPad to get this.

Or just do:

 const arraySlot = ethers.utils.keccak256("0x0000000000000000000000000000000000000000000000000000000000000002");

Which should be location: 0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace

Mapping Stuff

Same issue as array stuff. You'll be looking for location: 0x3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff

Debugging Stuff in the future

In the future, you can actually "trace" all transactions and see the storage updates yourself with something like this:

    const trace = await network.provider.send("debug_traceTransaction", [
        funWithStorage.transactionHash,
    ])
    for (structLog in trace.structLogs) {
        if (trace.structLogs[structLog].op == "SSTORE") {
            console.log(trace.structLogs[structLog])
        }
    }

This will print out every time the SSTORE opcode is called, and you'll be able to see what storage looks like!

abrel commented 2 years ago

Many thanks :) It works perfectly after adding the padding ! The debug_traceTransaction is super helpful.