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

3 Answers 3

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

0

Here's a contract for demonstration.

pragma solidity 0.8;

contract Cont {
    uint a = 5;
    uint[2] public numbers; // Fixed array with max elements 2 
    uint[] public nums;   // Dynamic array
    uint b = 6;

    function readStorageSlot(uint256 i) public view returns (bytes32 content) {
        assembly {
            content := sload(i) 
        }
    }

    function pushNumbers() public {
        numbers = [9,1]; // Just adding two numbers to the fixed array
    }

    function setNums(uint y) public {
        nums.push(y); // To add numbers to the dynamic array.
    }

    function getNums() public view returns(uint[] memory) {
        return nums;
    }

    function getLocationOfArray(uint slotNumOfDynamicArray) public pure returns (uint slot) {
        return uint256(keccak256(abi.encode(slotNumOfDynamicArray)));
    }
}

Slot - Value Stored

Slot0 - 5 (uint a)

Slot1 - 9 (numbers[0] - first record)

Slot2 - 1 (numbers[1] - second record)

Slot3 - 0 (Size of the dynamic nums array)

Slot4 - 6 (uint b)


As we can see that for fixed arrays the value is stored in their respective slots but what about dynamic arrays?

Let's add a few numbers to the nums dynamic array , say 38,29,6,2

Now the size of the dynamic array becomes 4 so the value stored in slot3 changes from 0 to 4.

Now these elements are going to stored in slot after hashing the slot number of the nums array which is 3.

  • If we hash it using the getLocationOfArray(3) function we get the output as :

87903029871075914254377627908054574944891091886930582284385770809450030037083

Which is the starting of the slot from where the elements in the array is going to be stored.

Value can be read from slots using the readStorageSlot() function.

So the first element that is 38 is going to be stored in this slot.

87903029871075914254377627908054574944891091886930582284385770809450030037083 - 38

87903029871075914254377627908054574944891091886930582284385770809450030037084 - 29

87903029871075914254377627908054574944891091886930582284385770809450030037085 - 6

87903029871075914254377627908054574944891091886930582284385770809450030037086 - 2


Now if we add another element to the array it's going to be stored in the next slot which is 87903029871075914254377627908054574944891091886930582284385770809450030037087

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.