简体   繁体   中英

How does solidity store arrays in storage? and how to decode them?

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM