Skip to content

Commit 05b0efb

Browse files
Storage layout specifier docs
1 parent 07b202c commit 05b0efb

File tree

3 files changed

+106
-0
lines changed

3 files changed

+106
-0
lines changed

docs/contracts.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ There is no "cron" concept in Ethereum to call a function at a particular event
2323
.. include:: contracts/transient-storage.rst
2424

2525
.. include:: contracts/constant-state-variables.rst
26+
.. include:: contracts/custom-storage-layout.rst
2627
.. include:: contracts/functions.rst
2728

2829
.. include:: contracts/events.rst
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.. index:: ! custom storage layout, ! storage layout specifier, ! layout at, ! base slot
2+
3+
.. _custom-storage-layout:
4+
5+
*********************
6+
Custom Storage Layout
7+
*********************
8+
9+
Contracts can define an arbitrary base slot for its own storage.
10+
The contract's state variables, including those inherited from base contracts,
11+
will be stored from the specified slot instead of the default slot zero.
12+
13+
.. code-block:: solidity
14+
15+
// SPDX-License-Identifier: GPL-3.0
16+
pragma solidity ^0.8.29;
17+
18+
contract C layout at 0xABCD + 0x1234 { }
19+
20+
As the previous example shows, this can be done by using ``layout at <base-slot-expression>``
21+
in the header of a contract definition.
22+
23+
The layout specifier can be placed either before or after the inheritance specifier, and at most once.
24+
The ``base-slot-expression`` must be an :ref:`integer literal<rational_literals>` expression
25+
that can be evaluated at compile time and yield a value in the range of ``uint256``.
26+
27+
In the case of a custom storage layout specification which places the contract near the storage end,
28+
the number of slots available for static objects is determined by ``max storage size - base slot`` and
29+
the compiler can detect whether the contract extends past the end.
30+
For dynamic typed variables, they are allocated in random locations of the storage, including those before
31+
the layout base slot.
32+
It is also important to mention that, in cases where the contract is near the end of storage, there are
33+
risks related to upgradeability and inline assembly access beyond allocated space.
34+
35+
The location of a contract's state variable is determined by its position in the hierarchy tree.
36+
Inherited variables will be stored before the state variables declared by the contract itself and
37+
that changes the slots where they should be placed.
38+
Similarly, when a contract specifies a custom storage layout, not only its own storage variables are shifted,
39+
but also all other variables from contracts in the same inheritance tree.
40+
Thus, the storage layout can only be specified at the top most contract of the inheritance tree, assuring
41+
that all contracts of the tree have their layout base properly adjusted.
42+
43+
The storage layout cannot be specified for abstract contracts, interfaces and libraries.
44+
Also, it is important to note that it does **not** affect transient state variables.
45+
46+
Further details are explained later when :ref:`layout of storage variables<storage-inplace-encoding>` are described.
47+
48+
.. warning::
49+
The identifiers ``layout`` and ``at`` are not reserved keywords of the Solidity language, but
50+
it is strongly recommended to avoid using them since that may change in the future.

docs/internals/layout_in_storage.rst

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,61 @@ by the above rules, state variables from different contracts do share the same s
3434
The elements of structs and arrays are stored after each other, just as if they were given
3535
as individual values.
3636

37+
If a contract specifies a :ref:`custom storage layout<custom-storage-layout>`, the slots which
38+
the state variables occupy are shifted according the value defined as the layout base.
39+
The custom layout is specified in the most derived contract and, following the order explained
40+
above, starting from the most base-ward contract's variables, all storage slots are adjusted.
41+
42+
In the following example, contract ``C`` inherits from contracts ``A`` and ``B`` and also
43+
specifies a custom storage base slot.
44+
The result is that all variable storage slots of the inheritance tree will be adjusted according to
45+
the value specified by ``C``.
46+
47+
.. code-block:: solidity
48+
49+
// SPDX-License-Identifier: GPL-3.0
50+
pragma solidity ^0.8.29;
51+
52+
struct S {
53+
int32 a;
54+
bool b;
55+
}
56+
57+
contract A {
58+
uint x;
59+
uint transient y;
60+
uint constant w = 10;
61+
uint immutable z = 12;
62+
}
63+
64+
contract B {
65+
uint16 i;
66+
uint16 j;
67+
S s;
68+
int8 k;
69+
}
70+
71+
contract C is A, B layout at 42 {
72+
uint8[10] register;
73+
bool flag;
74+
}
75+
76+
In the example, the storage layout starts with the inherited
77+
state variable ``x`` stored at the specified base slot ``42``.
78+
Transient, constant and immutable variables are stored in separate
79+
locations and, thus, ``y``, ``w`` and ``z``don't interfere with the layout.
80+
The next variables ``i`` and ``j`` need 2 bytes each and can be packed in
81+
the next slot ``43``, at offsets ``0`` and ``2`` respectively.
82+
Since ``s`` is a struct, its two members are packed contiguously, demanding
83+
both 5 bytes.
84+
Even though they still could fit in slot ``43``, structs and static arrays
85+
always start a new slot as well as any other item after them.
86+
So, according to that ``s`` is placed at slot ``44`` and the next variable,
87+
``k`` at slot ``45``.
88+
Then, variable ``register`` which is an array of 10 positions, starts at slot ``46``
89+
and demands 80 bytes. Finally, variable, ``flag``, start at slot ``47``, because,
90+
as explained before, variables after structs and arrays always start a new slot.
91+
3792
.. warning::
3893
When using elements that are smaller than 32 bytes, your contract's gas usage may be higher.
3994
This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller

0 commit comments

Comments
 (0)