Skip to content

Commit 0598c01

Browse files
authored
Merge pull request #15892 from ethereum/storageLayoutSpecifierDocs
Storage layout specifier docs
2 parents 4bf8a9f + de966a7 commit 0598c01

File tree

3 files changed

+163
-1
lines changed

3 files changed

+163
-1
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: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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+
A contract can define an arbitrary location for its storage using the ``layout`` specifier.
10+
The contract's state variables, including those inherited from base contracts,
11+
start from the specified base 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 0xAAAA + 0x11 {
19+
uint[3] x; // Occupies slots 0xAABB..0xAABD
20+
}
21+
22+
As the above example shows, the specifier uses the ``layout at <base-slot-expression>`` syntax
23+
and is located in the header of a contract definition.
24+
25+
The layout specifier can be placed either before or after the inheritance specifier, and can appear at most once.
26+
The ``base-slot-expression`` must be an :ref:`integer literal<rational_literals>` expression
27+
that can be evaluated at compilation time and yields a value in the range of ``uint256``.
28+
29+
A custom layout cannot make contract's storage "wrap around".
30+
If the selected base slot would push the static variables past the end of storage,
31+
the compiler will issue an error.
32+
Note that the data areas of dynamic arrays and mappings are not affected by this check because
33+
their layout is not linear.
34+
Regardless of the base slot used, their locations are calculated in a way that always puts them
35+
within the range of ``uint256`` and their sizes are not known at compilation time.
36+
37+
While there are no other limits placed on the base slot, it is recommended to avoid locations that are
38+
too close to the end of the address space.
39+
Leaving too little space may complicate contract upgrades or cause problems for contracts that store
40+
additional values past their allocated space using inline assembly.
41+
42+
The storage layout can only be specified for the topmost contract of an inheritance tree, and
43+
affects locations of all the storage variables in all the contracts in that tree.
44+
Variables are laid out according to the order of their definitions and the
45+
positions of their contracts in the :ref:`linearized inheritance hierarchy<multi-inheritance>`
46+
and a custom base slot preserves their relative positions, shifting them all by the same amount.
47+
48+
The storage layout cannot be specified for abstract contracts, interfaces and libraries.
49+
Also, it is important to note that it does *not* affect transient state variables.
50+
51+
For details about storage layout and the effect of the layout specifier on it see
52+
:ref:`layout of storage variables<storage-inplace-encoding>`.
53+
54+
.. warning::
55+
The identifiers ``layout`` and ``at`` are not yet reserved as keywords in the language.
56+
It is strongly recommended to avoid using them since they will become reserved in a future
57+
breaking release.

docs/internals/layout_in_storage.rst

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,110 @@ 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 assigned
38+
to static storage variables are shifted according the value defined as the layout base.
39+
Locations of dynamic arrays and mappings are also indirectly affected by this due to shifting
40+
of the static slots they are based on.
41+
The custom layout is specified in the most derived contract and, following the order explained
42+
above, starting from the most base-ward contract's variables, all storage slots are adjusted.
43+
44+
In the following example, contract ``C`` inherits from contracts ``A`` and ``B`` and also
45+
specifies a custom storage base slot.
46+
The result is that all storage variable slots of the inheritance tree are adjusted according to
47+
the value specified by ``C``.
48+
49+
.. code-block:: solidity
50+
51+
// SPDX-License-Identifier: GPL-3.0
52+
pragma solidity ^0.8.29;
53+
54+
struct S {
55+
int32 x;
56+
bool y;
57+
}
58+
59+
contract A {
60+
uint a;
61+
uint128 transient b;
62+
uint constant c = 10;
63+
uint immutable d = 12;
64+
}
65+
66+
contract B {
67+
uint8[] e;
68+
mapping(uint => S) f;
69+
uint16 g;
70+
uint16 h;
71+
bytes16 transient i;
72+
S s;
73+
int8 k;
74+
}
75+
76+
contract C is A, B layout at 42 {
77+
bytes21 l;
78+
uint8[10] m;
79+
bytes5[8] n;
80+
bytes5 o;
81+
}
82+
83+
In the example, the storage layout starts with the inherited
84+
state variable ``a`` stored directly inside the base slot (slot ``42``).
85+
Transient, constant and immutable variables are stored in separate
86+
locations, and thus, ``b``, ``i``, ``c`` and ``d`` have no effect on the storage layout.
87+
Then we get to the dynamic array ``e`` and mapping ``f``.
88+
They both reserve a whole slot whose address will be used to :ref:`calculate<storage-hashed-encoding>`
89+
the location where their data is actually stored.
90+
The slot cannot be shared with any other variable, because the resulting addresses must be unique.
91+
The next two variables, ``g`` and ``h``, need 2 bytes each and can be packed together into
92+
slot ``45``, at offsets ``0`` and ``2`` respectively.
93+
Since ``s`` is a struct, its two members are packed contiguously, each taking up 5 bytes.
94+
Even though they both would still fit in slot ``45``, structs and arrays always start a new slot.
95+
Therefore, ``s`` is placed in slot ``46`` and the next variable, ``k``, in slot ``47``.
96+
Base contracts, on the other hand, can share slots with derived ones, so ``l`` does not require an new one.
97+
Then variable ``m``, which is an array of 10 items, gets into slot ``48`` and takes up 10 bytes.
98+
``n`` is an array as well, but due to the size of its items, cannot fill its first slot perfectly
99+
and spills over to the next one.
100+
Finally, variable ``o`` ends up in slot ``51``, even though it is of the same type as items of ``n``.
101+
As explained before, variables after structs and arrays always start a new slot.
102+
103+
Putting it all together, the storage and transient storage layouts of contract ``C`` can be illustrated as follows:
104+
105+
- Storage:
106+
::
107+
108+
42 [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
109+
43 [eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee]
110+
44 [ffffffffffffffffffffffffffffffff]
111+
45 [ hhgg]
112+
46 [ yxxxx]
113+
47 [ lllllllllllllllllllllk]
114+
48 [ mmmmmmmmmm]
115+
49 [ nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn]
116+
50 [ nnnnnnnnnn]
117+
51 [ ooooo]
118+
119+
- Transient storage:
120+
::
121+
122+
00 [iiiiiiiiiiiiiiiibbbbbbbbbbbbbbbb]
123+
124+
Note that the storage specifier affects ``A`` and ``B`` only as a part of ``C``'s inheritance hierarchy.
125+
When deployed independently, their storage starts at ``0``:
126+
127+
- Storage layout of ``A``:
128+
::
129+
130+
00 [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
131+
132+
- Storage layout of ``B``:
133+
::
134+
135+
00 [eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee]
136+
01 [ffffffffffffffffffffffffffffffff]
137+
02 [ hhgg]
138+
03 [ yxxxx]
139+
04 [ k]
140+
37141
.. warning::
38142
When using elements that are smaller than 32 bytes, your contract's gas usage may be higher.
39143
This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller
@@ -463,4 +567,4 @@ Transient Storage Layout
463567
"numberOfBytes": "32"
464568
}
465569
}
466-
}
570+
}

0 commit comments

Comments
 (0)