Solidity – Location of Dynamic Array Values in Smart Contract Storage

arrayssoliditystorage

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)
        }

    }


}

Best Answer

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!

Related Topic