Storage of Arrays and Mappings
Fixed array
behaves just like typical value type variables
Structs and array data always start a new slot and their items are packed tightly according to these rules.
contract StorageComplex {
uint256 a; //slot 0
uint256[3] fixedArray; //slot 1,2,3: 99,999,9999
uint256 b; //slot 4
constructor() {
fixedArray = [99, 999, 9999];
}
function getArraySlot() external pure returns(uint256 ret) {
assembly {
ret := fixedArray.slot //returns 1
}
}
function getVarSlot() external pure returns(uint256 ret) {
assembly {
ret := b.slot //returns 4
}
}
// to get individual elements
function fixedArrayView(uint256 index) external view returns (uint256 ret) {
assembly {
ret := sload(add(fixedArray.slot, index))
}
}
}
getArraySlot()
-> returns 1 -> storage slot 1returns the first slot the array occupies
fixedArray
occupies storage slot 1 to 3: 1 slot for each of its uint256 elements
fixedArrayView()
-> index: {0,2}index(0): 99
index(1): 999
basically we are incrementing the slot by the index, to move to the appropriate storage slot containing the element
Dynamic array
elements will not be stored sequentially down the slots, like a fixed array.
cos' it could expand, overrun and crash into something below it.
length of the dynamic array is stored at its slot ->
sload(bigArray.slot)
Array data is located starting at
keccak256(array.slot)
contract StorageComplex2 {
uint256[3] fixedArray; //slot: 0,1,2
uint256[] bigArray; //slot: 3
uint8[] smallArray;
constructor() {
fixedArray = [99, 999, 9999];
bigArray = [10, 20, 30, 40];
smallArray = [1, 2, 3];
}
// returns slot of big array
function getBigArraySlot() external pure returns(uint256 ret) {
assembly {
ret := bigArray.slot //returns 3
}
}
//returns length of dynamic array
function bigArrayLength() external view returns (uint256 ret) {
assembly {
ret := sload(bigArray.slot) //returns 4
}
}
// get elements in big array
function readBigArrayLocation(uint256 index) external view returns (uint256 ret) {
uint256 slot;
assembly {
slot := bigArray.slot
}
bytes32 location = keccak256(abi.encode(slot));
assembly {
ret := sload(add(location, index))
}
}
}
elements are stored at different storage location: keccak256 hash of the slot
to read elements sequentially, add indexed to the location to traverse down the list of elements
uint8[] smallArray
contract StorageComplex2 {
uint256[3] fixedArray; //slot: 0,1,2
uint256[] bigArray; //slot: 3
uint8[] smallArray;
constructor() {
fixedArray = [99, 999, 9999];
bigArray = [10, 20, 30, 40];
smallArray = [1, 2, 3];
}
// returns length of array: 3
function readSmallArray() external view returns (uint256 ret) {
assembly {
ret := sload(smallArray.slot)
}
}
// returns elements in array
function readSmallArrayLocation(uint256 index) external view returns (bytes32 ret) {
uint256 slot;
assembly {
slot := smallArray.slot
}
bytes32 location = keccak256(abi.encode(slot));
assembly {
ret := sload(add(location, index))
}
}
}
since uint8 is 1 bytes, all 3 elements are packed into the first storage slot at location
all elements stored at index 0, nothing at index 1.

Mappings
like dynamic arrays location of elements are stored elswhere
location: hash of the concatenation the key with the storage slot
bytes32 location = keccak256(abi.encode(key, uint256(slot)));
contract StorageComplex2 {
mapping(uint256 => uint256) public myMapping;
mapping(uint256 => mapping(uint256 => uint256)) public nestedMapping;
mapping(address => uint256[]) public addressToList;
constructor() {
myMapping[10] = 5;
myMapping[11] = 6;
nestedMapping[2][4] = 7;
addressToList[0x5B38Da6a701c568545dCfcB03FcB875f56beddC4] = [
42,
1337,
777
];
}
// returns values in a mapping
function getMapping(uint256 key) external view returns (uint256 ret) {
uint256 slot;
assembly {
slot := myMapping.slot
}
bytes32 location = keccak256(abi.encode(key, uint256(slot)));
assembly {
ret := sload(location)
}
}
// nestedMapping[2][4] = 7;
function getNestedMapping() external view returns (uint256 ret) {
uint256 slot;
assembly {
slot := nestedMapping.slot
}
bytes32 location = keccak256(abi.encode(uint256(4),
keccak256(abi.encode(uint256(2), uint256(slot)))
)
);
assembly {
ret := sload(location)
}
}
// mapping(address => uint256[]) public addressToList;
function lengthOfNestedList() external view returns (uint256 ret) {
uint256 addressToListSlot;
assembly {
addressToListSlot := addressToList.slot
}
bytes32 location = keccak256(
abi.encode(
address(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4),
uint256(addressToListSlot)
)
);
assembly {
ret := sload(location)
}
}
function getAddressToList(uint256 index) external view returns (uint256 ret) {
uint256 slot;
assembly {
slot := addressToList.slot
}
bytes32 location = keccak256(
abi.encode(
keccak256(
abi.encode(
address(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4),
uint256(slot)
)
)
)
);
assembly {
ret := sload(add(location, index))
}
}
}
Last updated