I have this contract below
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract FunWithStorage {
uint256 public favoriteNumber = 20; // Stored at slot 0
string private test = "hello1adsfdsfds";
bool public someBool = false; // Stored at slot 1
uint256[] public myArray;
uint256[] public testArray;
bool public testing = true;
/* 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)
public 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 = false; // See stored spot above // SSTORE
myArray.push(222); // SSTORE
myArray.push(201); // SSTORE
myArray.push(220); // SSTORE
testArray.push(100);
testArray.push(100);
testArray.push(100);
myMap[0] = true; // SSTORE
myMap[1] = true; // SSTORE
myMap[2] = true; // SSTORE
i_not_in_storage = 123;
}
}
These are the storage locations of the objects
Location 0: 0x0000000000000000000000000000000000000000000000000000000000000019
Location 1: 0x68656c6c6f31616473666473666473000000000000000000000000000000001e
Location 2: 0x0000000000000000000000000000000000000000000000000000000000000000
Location 3: 0x0000000000000000000000000000000000000000000000000000000000000003
Location 4: 0x0000000000000000000000000000000000000000000000000000000000000003
Location 5: 0x0000000000000000000000000000000000000000000000000000000000000001
Location 6: 0x0000000000000000000000000000000000000000000000000000000000000000
Location 7: 0x0000000000000000000000000000000000000000000000000000000000000000
Location 8: 0x0000000000000000000000000000000000000000000000000000000000000000
Location 9: 0x0000000000000000000000000000000000000000000000000000000000000000
Location 2 and 3 are both arrays and if I do the following
const firstelementLocation = ethers.utils.keccak256(
"0x0000000000000000000000000000000000000000000000000000000000000003"
)
const arrayElement = await ethers.provider.getStorageAt(
funWithStorage.address,
firstelementLocation
)
console.log(parseInt(arrayElement), "value of first element")
I can get the value of the first value in the array at location 3. My questions are how do I get the first value in the array at location 4 and how do i get the next value in the array?
I have tried looking at other questions
You have your slot calculation wrong:
contract FunWithStorage {
uint256 public favoriteNumber = 20; // Stored at slot 0
// as the length < 30bytes: the whole string is in slot 1, otherwise the slot would contain just the strings length
string private test = "hello1adsfdsfds"; // Stored at slot 1
bool public someBool = false; // Stored at slot 2
uint256[] public myArray; // Stored LENGTH at slot 3
}
// example: arrays slot
let ARRAY_SLOT = 3;
// example: array index: 0,1,2, ...
let ITEM_SLOT = 0;
// Get the Length of the array
let length = BigInt(await getStorageAt(ARRAY_SLOT));
// Get the location of the array's item
let location = BigInt(keccak256(encodePacked(ARRAY_SLOT))) + BigInt(ITEM_SLOT);
let memory = await getStorageAt(location);
In your example, the array item takes 1 SLOT. But what the location would be, if it was a Struct:
contract Foo {
type User {
address owner;
uint balance;
}
User[] users;
}
With getStorageAt
your read one SLOT, so you can't get the array item at once, as it now occupies 2 SLOTS - your read owner
and balance
of the item separately. In the struct the order matters: owner = 0
, balance = 1
, ... So ITEM_SLOT
would be not just the arrays index, but now you have to take into account, that the size of a single item is 2
let ITEM_SLOT = ARRAY_ITEM_INDEX * ITEM_SIZE + ITEM_INDEX
// users[3].balance:
let ITEM_SLOT = 3 * 2 + 1;