.. index:: storage, state variable, mapping ************************************ Layout of State Variables in Storage ************************************ .. _storage-inplace-encoding: Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules: - The first item in a storage slot is stored lower-order aligned. - Elementary types use only as many bytes as are necessary to store them. - If an elementary type does not fit the remaining part of a storage slot, it is moved to the next storage slot. - Structs and array data always start a new slot and occupy whole slots (but items inside a struct or array are packed tightly according to these rules). For contracts that use inheritance, the ordering of state variables is determined by the C3-linearized order of contracts starting with the most base-ward contract. If allowed by the above rules, state variables from different contracts do share the same storage slot. The elements of structs and arrays are stored after each other, just as if they were given explicitly. .. warning:: When using elements that are smaller than 32 bytes, your contract's gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size. It is only beneficial to use reduced-size arguments if you are dealing with storage values because the compiler will pack multiple elements into one storage slot, and thus, combine multiple reads or writes into a single operation. When dealing with function arguments or memory values, there is no inherent benefit because the compiler does not pack these values. Finally, in order to allow the EVM to optimize for this, ensure that you try to order your storage variables and ``struct`` members such that they can be packed tightly. For example, declaring your storage variables in the order of ``uint128, uint128, uint256`` instead of ``uint128, uint256, uint128``, as the former will only take up two slots of storage whereas the latter will take up three. .. note:: The layout of state variables in storage is considered to be part of the external interface of Solidity due to the fact that storage pointers can be passed to libraries. This means that any change to the rules outlined in this section is considered a breaking change of the language and due to its critical nature should be considered very carefully before being executed. Mappings and Dynamic Arrays =========================== .. _storage-hashed-encoding: Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash computation to find the starting position of the value or the array data. These starting positions are always full stack slots. The mapping or the dynamic array itself occupies a slot in storage at some position ``p`` according to the above rule (or by recursively applying this rule for mappings of mappings or arrays of arrays). For dynamic arrays, this slot stores the number of elements in the array (byte arrays and strings are an exception, see :ref:`below `). For mappings, the slot is unused (but it is needed so that two equal mappings after each other will use a different hash distribution). Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key ``k`` is located at ``keccak256(k . p)`` where ``.`` is concatenation. If the value is again a non-elementary type, the positions are found by adding an offset of ``keccak256(k . p)``. So for the following contract snippet the position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``:: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.0 <0.9.0; contract C { struct S { uint a; uint b; } uint x; mapping(uint => mapping(uint => S)) data; } .. _bytes-and-string: ``bytes`` and ``string`` ------------------------ ``bytes`` and ``string`` are encoded identically. For short byte arrays, they store their data in the same slot where the length is also stored. In particular: if the data is at most ``31`` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. For byte arrays that store data which is ``32`` or more bytes long, the main slot stores ``length * 2 + 1`` and the data is stored as usual in ``keccak256(slot)``. This means that you can distinguish a short array from a long array by checking if the lowest bit is set: short (not set) and long (set). .. note:: Handling invalidly encoded slots is currently not supported but may be added in the future. JSON Output =========== .. _storage-layout-top-level: The storage layout of a contract can be requested via the :ref:`standard JSON interface `. The output is a JSON object containing two keys, ``storage`` and ``types``. The ``storage`` object is an array where each element has the following form: .. code:: { "astId": 2, "contract": "fileA:A", "label": "x", "offset": 0, "slot": "0", "type": "t_uint256" } The example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA`` and - ``astId`` is the id of the AST node of the state variable's declaration - ``contract`` is the name of the contract including its path as prefix - ``label`` is the name of the state variable - ``offset`` is the offset in bytes within the storage slot according to the encoding - ``slot`` is the storage slot where the state variable resides or starts. This number may be very large and therefore its JSON value is represented as a string. - ``type`` is an identifier used as key to the variable's type information (described in the following) The given ``type``, in this case ``t_uint256`` represents an element in ``types``, which has the form: .. code:: { "encoding": "inplace", "label": "uint256", "numberOfBytes": "32", } where - ``encoding`` how the data is encoded in storage, where the possible values are: - ``inplace``: data is laid out contiguously in storage (see :ref:`above `). - ``mapping``: Keccak-256 hash-based method (see :ref:`above `). - ``dynamic_array``: Keccak-256 hash-based method (see :ref:`above `). - ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above `). - ``label`` is the canonical type name. - ``numberOfBytes`` is the number of used bytes (as a decimal string). Note that if ``numberOfBytes > 32`` this means that more than one slot is used. Some types have extra information besides the four above. Mappings contain its ``key`` and ``value`` types (again referencing an entry in this mapping of types), arrays have its ``base`` type, and structs list their ``members`` in the same format as the top-level ``storage`` (see :ref:`above `). .. note :: The JSON output format of a contract's storage layout is still considered experimental and is subject to change in non-breaking releases of Solidity. The following example shows a contract and its storage layout, containing value and reference types, types that are encoded packed, and nested types. .. code:: // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.0 <0.9.0; contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; mapping (uint => mapping (address => bool)) map; uint[] array; string s1; bytes b1; } .. code:: "storageLayout": { "storage": [ { "astId": 14, "contract": "fileA:A", "label": "x", "offset": 0, "slot": "0", "type": "t_uint256" }, { "astId": 16, "contract": "fileA:A", "label": "y", "offset": 0, "slot": "1", "type": "t_uint256" }, { "astId": 18, "contract": "fileA:A", "label": "s", "offset": 0, "slot": "2", "type": "t_struct(S)12_storage" }, { "astId": 20, "contract": "fileA:A", "label": "addr", "offset": 0, "slot": "6", "type": "t_address" }, { "astId": 26, "contract": "fileA:A", "label": "map", "offset": 0, "slot": "7", "type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))" }, { "astId": 29, "contract": "fileA:A", "label": "array", "offset": 0, "slot": "8", "type": "t_array(t_uint256)dyn_storage" }, { "astId": 31, "contract": "fileA:A", "label": "s1", "offset": 0, "slot": "9", "type": "t_string_storage" }, { "astId": 33, "contract": "fileA:A", "label": "b1", "offset": 0, "slot": "10", "type": "t_bytes_storage" } ], "types": { "t_address": { "encoding": "inplace", "label": "address", "numberOfBytes": "20" }, "t_array(t_uint256)2_storage": { "base": "t_uint256", "encoding": "inplace", "label": "uint256[2]", "numberOfBytes": "64" }, "t_array(t_uint256)dyn_storage": { "base": "t_uint256", "encoding": "dynamic_array", "label": "uint256[]", "numberOfBytes": "32" }, "t_bool": { "encoding": "inplace", "label": "bool", "numberOfBytes": "1" }, "t_bytes_storage": { "encoding": "bytes", "label": "bytes", "numberOfBytes": "32" }, "t_mapping(t_address,t_bool)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => bool)", "numberOfBytes": "32", "value": "t_bool" }, "t_mapping(t_uint256,t_mapping(t_address,t_bool))": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => mapping(address => bool))", "numberOfBytes": "32", "value": "t_mapping(t_address,t_bool)" }, "t_string_storage": { "encoding": "bytes", "label": "string", "numberOfBytes": "32" }, "t_struct(S)12_storage": { "encoding": "inplace", "label": "struct A.S", "members": [ { "astId": 2, "contract": "fileA:A", "label": "a", "offset": 0, "slot": "0", "type": "t_uint128" }, { "astId": 4, "contract": "fileA:A", "label": "b", "offset": 16, "slot": "0", "type": "t_uint128" }, { "astId": 8, "contract": "fileA:A", "label": "staticArray", "offset": 0, "slot": "1", "type": "t_array(t_uint256)2_storage" }, { "astId": 11, "contract": "fileA:A", "label": "dynArray", "offset": 0, "slot": "3", "type": "t_array(t_uint256)dyn_storage" } ], "numberOfBytes": "128" }, "t_uint128": { "encoding": "inplace", "label": "uint128", "numberOfBytes": "16" }, "t_uint256": { "encoding": "inplace", "label": "uint256", "numberOfBytes": "32" } } }