Storage

Storage Basics

  • .slot - returns the slot location (in uint256) that the variable is located in. Determined at compile-time and doesn’t change

  • sstore has no consideration for existing variables already declared or stored in the contract, so using it may override values that are already there

contract YulStorage {
	uint x = 2;  // slot 0 
	uint y = 5;  // slot 1 
	uint z = 10; // slot 2 
 
	function getXYul() external view returns (uint256 ret) {
        assembly {
            ret := sload(x.slot)
        }
    }

	function getVarYul(uint256 slot) external view returns (bytes32 ret) {
        assembly {
            ret := sload(slot)
        }
    }

		// risky, shouldn't be used unless you know what you're doing 
    function setVarYul(uint256 slot, uint256 value) external {
        assembly {
            sstore(slot, value)
        }
    }
} 

Storage Offsets & Bitshifting

  • Multiple variables can be stored within a slot

  • Calling readBySlot returns the below bytes32 data, where all variables C to F are stored in slot 0:

  • To derive the specific values with Yul, we have to use the .offset helper

  • .offset is the number of bytes to the left in a bytes32 object that we have to look in order to find the location of the variable we want

  • To obtain the value of E:

  • shr(x, y) - shift value y right by x number of bits

  • and is a bitwise operation that compares values bit by bit

    • 1 and 0 returns 0

    • 0 and F returns 0

    • 0 and 8 returns 8 → we get E = 8

  • In order to write to specific values in a slot (while not overriding all the values in the slot), we need to use bit masking & bit shifting

  • first load the slot that contains E

  • selectively delete the bytes that represent E by using bitwise and to conduct masking

  • shift the new E value by 28 bytes and conduct bitwise or to combine the E value and current slot values together

  • call sstore to set the new value into the storage slot

Storage of Arrays & Mappings

  • For fixed arrays, essentially the same as declaring 3 uint256 variables that will take up slots 0, 1 and 2.

  • For dynamic arrays, the data that’s being stored in the storage slot is the length of the array.

  • Items in a dynamic array will not be stored sequentially down the slots, like in a fixed array, because the array could overrun and clash with other variables in the following slots

  • Solidity stores values of dynamic arrays by taking the keccak256 hash of the array’s length, then adding the index of the value to the hash

  • For mappings, Solidity takes the hash of the mapping’s storage slot, and concatenates it with the mapping key, then stores the value in the corresponding slot location

  • Nested mappings: hashes of hashes

  • Keys have to be hashed in order of operation

Last updated