3

I am trying to better understand smart contract storage and the way the variable values are stored into it. And after some testing, I noticed that values for dynamic sized arrays are stored differently

Take, for example this dynamic array:

uint[] newArray = [5,6,7];
    
 // slot0 = 3;

If I read storage slot0 I will get value 3 (the size of the newArray)

However, with a statically defined array, the values are sorted per slots:

uint[3] newArray = [5,6,7];


// slot0 = 5;
// slot1 = 6;
// slot2 = 7;

My question is this: Where are the values of dynamic arrays stored in solidity? Which are their storage slots? After all, the data has to be stored somewhere. So which slot should I read to get a value from a dynamic array?

NOTE: Below is the code I used to test/experiment with contract storage.

//SPDX-License-Identifier: UNLICENCED

pragma solidity 0.8.3;  

contract ReadArrayStorage {

    uint[] newArray = [5,6,7];

     ///////////////// Main Functionality /////////////////////
    function updateArray(uint[] calldata recievedArray)public {
            newArray = recievedArray;
    }

    function readArray() public view returns (uint[] memory) {
        return newArray;

    }


    ///////////////// Assembly Functions for Reading Storage /////////////////////
    function updateSlot(uint256 slotNumber, uint256 newValue) public {

        assembly {
            sstore(slotNumber, newValue)
        }
    }

    function readSlot(uint slotNumber) public view returns (uint value) {
        assembly{
            value := sload(slotNumber)
        }

    }


}
1

2 Answers 2

5

Q) Where are the values of dynamic arrays stored in solidity?

A) A dynamically-sized array needs a place to store its size as well as its elements.

contract StorageTest {
    uint256 a;     // slot 0
    uint256[2] b;  // slots 1-2

    struct Entry {
        uint256 id;
        uint256 value;
    }
    Entry c;       // slots 3-4
    Entry[] d;     //slot 5
}

In the above code, the dynamically-sized array d is at slot 5, but the only thing that’s stored there is the size of d. The values in the array are stored consecutively starting at the hash of the slot.

The following Solidity function computes the location of an element of a dynamically-sized array:


//elementSize => Number of Bits element takes.
// Example: (uint128 => elementSize = 128)
function arrLocation(uint256 slot, uint256 index, uint256 elementSize) public pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(slot))) + (index * elementSize/256) ;
    }

So slot 5 of your contract memory keeps the length of your dynamic array, and the location of the first element is computed like so: hash(slot) + (index)

You can find a lot more information here.

Hope this helps!

1
  • The link you posted helped me a lot (even though there were some errors there), but I fixed them in the code you posted, so now it works splendid for me.
    – Sky
    Dec 8, 2022 at 11:08
1

Values of dynamic arrays are stored in slots that are computed using a Keccak-256 hash. Their storage slots are keccak256(p), where p is the slot after applying the slot layout rules as follows:

  • The first item in a storage slot is stored lower-order aligned.
  • Value types use only as many bytes as are necessary to store them.
  • If a value type does not fit the remaining part of a storage slot, it is stored in the next storage slot.
  • Structs and array data always start a new slot and their items are packed tightly according to these rules.
  • Items following struct or array data always start a new storage slot.

Array data is located starting at keccak256(p) and it is laid out in the same way as statically-sized array data would: One element after the other, potentially sharing storage slots if the elements are not longer than 16 bytes. Dynamic arrays of dynamic arrays apply this rule recursively. The location of element x[i][j], where the type of x is uint24[][], is computed as follows (again, assuming x itself is stored at slot p): The slot is keccak256(keccak256(p) + i) + floor(j / floor(256 / 24)) and the element can be obtained from the slot data v using (v >> ((j % floor(256 / 24)) * 24)) & type(uint24).max.

See: https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#storage-inplace-encoding

Your Answer

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

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