1

Is there a way to read private variables of a contract using the cast storage command in Foundry?

I know that even if a variable in a contract is declared private, its value can be read. So I'm trying to read the value using the cast storage command, but it seems to be reading the wrong value. I would like to know the correct way.

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

contract SampleContract {
  bool public locked = true;
  uint256 public ID = block.timestamp;
  uint8 private flattening = 10;
  uint8 private denomination = 255;
  uint16 private awkwardness = uint16(block.timestamp);
  bytes32[3] private data;
}

Assuming that there is a contract like the one above,

$ cast storage 0xddC77c1407A0cB33D4652C71F600baB6fa1f84D5 0
0x0000000000000000000000000000000000000000000000000000000000000001

By doing this, the value of the locked variable in Slot 0 can be read, and it was confirmed that the value was read correctly. However, when I tried to read the value of the flattening variable in Slot 2 as follows:

$ cast storage 0xddC77c1407A0cB33D4652C71F600baB6fa1f84D5 2
0x00000000000000000000000000000000000000000000000000000000119cff0a

Completely strange values are read. Since the value of 10 is assigned to the flattening variable in the code, I expect the value to be "0x000000000000000000000000000000000000000000000000000000000000000a," which is the hexadecimal value of decimal 10.

What is wrong?

1 Answer 1

2

Solidity tries to pack your state vars tightly as to not use as much storage space. From the docs "Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible".

This means that if 2 state vars require 128 bits each, solidity will pack them both into slot 0.

Most of your confusion should be able to cleared up reading the Layout of State Variables documentation.

In this specific example though:

  • locked is a bool and only requires 1 bit, so it is stored in slot 0.
  • ID requires 32 bytes, which will not fit in the remaining 255 bits in slot 0, so it is stored in slot 1 and takes it up fully.
  • flattening requires 1 byte so it is stored in slot 2 as 0x0a
  • denomination requires 1 bytes which can fit in slot 2 so it is also stored in slot 2 as 0xff
  • awkwardness requires 2 bytes which can fit in slot 2 so it is also stored in slot 2 as 0x119c
  • data is an array of length 3 each element requiring 32 bytes, so these are stored in slot 3, 4, and 5.

This is why your slot 2 has the extra data. You'll notice at the end there is the 0x0a as your are expecting, but next to it also contains the next two variables values.

2
  • I also just noticed that this is the Privacy challenge in ethernaut. If you're having trouble I'm sure there are some writeups out there that might be able to explain this better. Commented Apr 24, 2023 at 11:38
  • An awesome answer!
    – Mila A
    Commented Jun 5 at 15:38

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.