mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
814 lines
37 KiB
ReStructuredText
814 lines
37 KiB
ReStructuredText
#############
|
|
Miscellaneous
|
|
#############
|
|
|
|
.. 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 <bytes-and-string>`).
|
|
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``::
|
|
|
|
|
|
pragma solidity >=0.4.0 <0.8.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 <compiler-api>`. 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 <storage-inplace-encoding>`).
|
|
- ``mapping``: Keccak-256 hash-based method (see :ref:`above <storage-hashed-encoding>`).
|
|
- ``dynamic_array``: Keccak-256 hash-based method (see :ref:`above <storage-hashed-encoding>`).
|
|
- ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above <bytes-and-string>`).
|
|
|
|
- ``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
|
|
<storage-layout-top-level>`).
|
|
|
|
.. 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::
|
|
|
|
pragma solidity >=0.4.0 <0.8.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"
|
|
}
|
|
}
|
|
}
|
|
|
|
.. index: memory layout
|
|
|
|
****************
|
|
Layout in Memory
|
|
****************
|
|
|
|
Solidity reserves four 32-byte slots, with specific byte ranges (inclusive of endpoints) being used as follows:
|
|
|
|
- ``0x00`` - ``0x3f`` (64 bytes): scratch space for hashing methods
|
|
- ``0x40`` - ``0x5f`` (32 bytes): currently allocated memory size (aka. free memory pointer)
|
|
- ``0x60`` - ``0x7f`` (32 bytes): zero slot
|
|
|
|
Scratch space can be used between statements (i.e. within inline assembly). The zero slot
|
|
is used as initial value for dynamic memory arrays and should never be written to
|
|
(the free memory pointer points to ``0x80`` initially).
|
|
|
|
Solidity always places new objects at the free memory pointer and
|
|
memory is never freed (this might change in the future).
|
|
|
|
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this
|
|
is even true for ``byte[]``, but not for ``bytes`` and ``string``).
|
|
Multi-dimensional memory arrays are pointers to memory arrays. The length of a
|
|
dynamic array is stored at the first slot of the array and followed by the array
|
|
elements.
|
|
|
|
.. warning::
|
|
There are some operations in Solidity that need a temporary memory area
|
|
larger than 64 bytes and therefore will not fit into the scratch space.
|
|
They will be placed where the free memory points to, but given their
|
|
short lifetime, the pointer is not updated. The memory may or may not
|
|
be zeroed out. Because of this, one should not expect the free memory
|
|
to point to zeroed out memory.
|
|
|
|
While it may seem like a good idea to use ``msize`` to arrive at a
|
|
definitely zeroed out memory area, using such a pointer non-temporarily
|
|
without updating the free memory pointer can have unexpected results.
|
|
|
|
.. index: calldata layout
|
|
|
|
*******************
|
|
Layout of Call Data
|
|
*******************
|
|
|
|
The input data for a function call is assumed to be in the format defined by the :ref:`ABI
|
|
specification <ABI>`. Among others, the ABI specification requires arguments to be padded to multiples of 32
|
|
bytes. The internal function calls use a different convention.
|
|
|
|
Arguments for the constructor of a contract are directly appended at the end of the
|
|
contract's code, also in ABI encoding. The constructor will access them through a hard-coded offset, and
|
|
not by using the ``codesize`` opcode, since this of course changes when appending
|
|
data to the code.
|
|
|
|
|
|
.. index: variable cleanup
|
|
|
|
*********************************
|
|
Internals - Cleaning Up Variables
|
|
*********************************
|
|
|
|
When a value is shorter than 256 bit, in some cases the remaining bits
|
|
must be cleaned.
|
|
The Solidity compiler is designed to clean such remaining bits before any operations
|
|
that might be adversely affected by the potential garbage in the remaining bits.
|
|
For example, before writing a value to memory, the remaining bits need
|
|
to be cleared because the memory contents can be used for computing
|
|
hashes or sent as the data of a message call. Similarly, before
|
|
storing a value in the storage, the remaining bits need to be cleaned
|
|
because otherwise the garbled value can be observed.
|
|
|
|
On the other hand, we do not clean the bits if the immediately
|
|
following operation is not affected. For instance, since any non-zero
|
|
value is considered ``true`` by ``JUMPI`` instruction, we do not clean
|
|
the boolean values before they are used as the condition for
|
|
``JUMPI``.
|
|
|
|
In addition to the design principle above, the Solidity compiler
|
|
cleans input data when it is loaded onto the stack.
|
|
|
|
Different types have different rules for cleaning up invalid values:
|
|
|
|
+---------------+---------------+-------------------+
|
|
|Type |Valid Values |Invalid Values Mean|
|
|
+===============+===============+===================+
|
|
|enum of n |0 until n - 1 |exception |
|
|
|members | | |
|
|
+---------------+---------------+-------------------+
|
|
|bool |0 or 1 |1 |
|
|
+---------------+---------------+-------------------+
|
|
|signed integers|sign-extended |currently silently |
|
|
| |word |wraps; in the |
|
|
| | |future exceptions |
|
|
| | |will be thrown |
|
|
| | | |
|
|
| | | |
|
|
+---------------+---------------+-------------------+
|
|
|unsigned |higher bits |currently silently |
|
|
|integers |zeroed |wraps; in the |
|
|
| | |future exceptions |
|
|
| | |will be thrown |
|
|
+---------------+---------------+-------------------+
|
|
|
|
.. index:: optimizer, common subexpression elimination, constant propagation
|
|
|
|
*************************
|
|
Internals - The Optimiser
|
|
*************************
|
|
|
|
This section discusses the optimiser that was first added to Solidity,
|
|
which operates on opcode streams. For information on the new Yul-based optimiser,
|
|
please see the `readme on github <https://github.com/ethereum/solidity/blob/develop/libyul/optimiser/README.md>`_.
|
|
|
|
The Solidity optimiser operates on assembly. It splits the sequence of instructions into basic blocks
|
|
at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the optimiser
|
|
analyses the instructions and records every modification to the stack,
|
|
memory, or storage as an expression which consists of an instruction and
|
|
a list of arguments which are pointers to other expressions. The optimiser
|
|
uses a component called "CommonSubexpressionEliminator" that amongst other
|
|
tasks, finds expressions that are always equal (on every input) and combines
|
|
them into an expression class. The optimiser first tries to find each new
|
|
expression in a list of already known expressions. If this does not work,
|
|
it simplifies the expression according to rules like
|
|
``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is
|
|
a recursive process, we can also apply the latter rule if the second factor
|
|
is a more complex expression where we know that it always evaluates to one.
|
|
Modifications to storage and memory locations have to erase knowledge about
|
|
storage and memory locations which are not known to be different. If we first
|
|
write to location x and then to location y and both are input variables, the
|
|
second could overwrite the first, so we do not know what is stored at x after
|
|
we wrote to y. If simplification of the expression x - y evaluates to a
|
|
non-zero constant, we know that we can keep our knowledge about what is stored at x.
|
|
|
|
After this process, we know which expressions have to be on the stack at
|
|
the end, and have a list of modifications to memory and storage. This information
|
|
is stored together with the basic blocks and is used to link them. Furthermore,
|
|
knowledge about the stack, storage and memory configuration is forwarded to
|
|
the next block(s). If we know the targets of all ``JUMP`` and ``JUMPI`` instructions,
|
|
we can build a complete control flow graph of the program. If there is only
|
|
one target we do not know (this can happen as in principle, jump targets can
|
|
be computed from inputs), we have to erase all knowledge about the input state
|
|
of a block as it can be the target of the unknown ``JUMP``. If the optimiser
|
|
finds a ``JUMPI`` whose condition evaluates to a constant, it transforms it
|
|
to an unconditional jump.
|
|
|
|
As the last step, the code in each block is re-generated. The optimiser creates
|
|
a dependency graph from the expressions on the stack at the end of the block,
|
|
and it drops every operation that is not part of this graph. It generates code
|
|
that applies the modifications to memory and storage in the order they were
|
|
made in the original code (dropping modifications which were found not to be
|
|
needed). Finally, it generates all values that are required to be on the
|
|
stack in the correct place.
|
|
|
|
These steps are applied to each basic block and the newly generated code
|
|
is used as replacement if it is smaller. If a basic block is split at a
|
|
``JUMPI`` and during the analysis, the condition evaluates to a constant,
|
|
the ``JUMPI`` is replaced depending on the value of the constant. Thus code like
|
|
|
|
::
|
|
|
|
uint x = 7;
|
|
data[7] = 9;
|
|
if (data[x] != x + 2)
|
|
return 2;
|
|
else
|
|
return 1;
|
|
|
|
still simplifies to code which you can compile even though the instructions contained
|
|
a jump in the beginning of the process:
|
|
|
|
::
|
|
|
|
data[7] = 9;
|
|
return 1;
|
|
|
|
.. index:: source mappings
|
|
|
|
***************
|
|
Source Mappings
|
|
***************
|
|
|
|
As part of the AST output, the compiler provides the range of the source
|
|
code that is represented by the respective node in the AST. This can be
|
|
used for various purposes ranging from static analysis tools that report
|
|
errors based on the AST and debugging tools that highlight local variables
|
|
and their uses.
|
|
|
|
Furthermore, the compiler can also generate a mapping from the bytecode
|
|
to the range in the source code that generated the instruction. This is again
|
|
important for static analysis tools that operate on bytecode level and
|
|
for displaying the current position in the source code inside a debugger
|
|
or for breakpoint handling. This mapping also contains other information,
|
|
like the jump type and the modifier depth (see below).
|
|
|
|
Both kinds of source mappings use integer identifiers to refer to source files.
|
|
The identifier of a source file is stored in
|
|
``output['sources'][sourceName]['id']`` where ``output`` is the output of the
|
|
standard-json compiler interface parsed as JSON.
|
|
|
|
.. note ::
|
|
In the case of instructions that are not associated with any particular source file,
|
|
the source mapping assigns an integer identifier of ``-1``. This may happen for
|
|
bytecode sections stemming from compiler-generated inline assembly statements.
|
|
|
|
The source mappings inside the AST use the following
|
|
notation:
|
|
|
|
``s:l:f``
|
|
|
|
Where ``s`` is the byte-offset to the start of the range in the source file,
|
|
``l`` is the length of the source range in bytes and ``f`` is the source
|
|
index mentioned above.
|
|
|
|
The encoding in the source mapping for the bytecode is more complicated:
|
|
It is a list of ``s:l:f:j:m`` separated by ``;``. Each of these
|
|
elements corresponds to an instruction, i.e. you cannot use the byte offset
|
|
but have to use the instruction offset (push instructions are longer than a single byte).
|
|
The fields ``s``, ``l`` and ``f`` are as above. ``j`` can be either
|
|
``i``, ``o`` or ``-`` signifying whether a jump instruction goes into a
|
|
function, returns from a function or is a regular jump as part of e.g. a loop.
|
|
The last field, ``m``, is an integer that denotes the "modifier depth". This depth
|
|
is increased whenever the placeholder statement (``_``) is entered in a modifier
|
|
and decreased when it is left again. This allows debuggers to track tricky cases
|
|
like the same modifier being used twice or multiple placeholder statements being
|
|
used in a single modifier.
|
|
|
|
In order to compress these source mappings especially for bytecode, the
|
|
following rules are used:
|
|
|
|
- If a field is empty, the value of the preceding element is used.
|
|
- If a ``:`` is missing, all following fields are considered empty.
|
|
|
|
This means the following source mappings represent the same information:
|
|
|
|
``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2``
|
|
|
|
``1:2:1;:9;2:1:2;;``
|
|
|
|
***************
|
|
Tips and Tricks
|
|
***************
|
|
|
|
* Use ``delete`` on arrays to delete all its elements.
|
|
* Use shorter types for struct elements and sort them such that short types are
|
|
grouped together. This can lower the gas costs as multiple ``SSTORE`` operations
|
|
might be combined into a single (``SSTORE`` costs 5000 or 20000 gas, so this is
|
|
what you want to optimise). Use the gas price estimator (with optimiser enabled) to check!
|
|
* Make your state variables public - the compiler creates :ref:`getters <visibility-and-getters>` for you automatically.
|
|
* If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`.
|
|
* Initialize storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});``
|
|
|
|
.. note::
|
|
If the storage struct has tightly packed properties, initialize it with separate
|
|
assignments: ``x.a = 1; x.b = 2;``. In this way it will be easier for the
|
|
optimizer to update storage in one go, thus making assignment cheaper.
|
|
|
|
**********
|
|
Cheatsheet
|
|
**********
|
|
|
|
.. index:: precedence
|
|
|
|
.. _order:
|
|
|
|
Order of Precedence of Operators
|
|
================================
|
|
|
|
The following is the order of precedence for operators, listed in order of evaluation.
|
|
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| Precedence | Description | Operator |
|
|
+============+=====================================+============================================+
|
|
| *1* | Postfix increment and decrement | ``++``, ``--`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | New expression | ``new <typename>`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Array subscripting | ``<array>[<index>]`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Member access | ``<object>.<member>`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Function-like call | ``<func>(<args...>)`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Parentheses | ``(<statement>)`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *2* | Prefix increment and decrement | ``++``, ``--`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Unary minus | ``-`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Unary operations | ``delete`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Logical NOT | ``!`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Bitwise NOT | ``~`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *3* | Exponentiation | ``**`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *4* | Multiplication, division and modulo | ``*``, ``/``, ``%`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *5* | Addition and subtraction | ``+``, ``-`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *6* | Bitwise shift operators | ``<<``, ``>>`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *7* | Bitwise AND | ``&`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *8* | Bitwise XOR | ``^`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *9* | Bitwise OR | ``|`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *10* | Inequality operators | ``<``, ``>``, ``<=``, ``>=`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *11* | Equality operators | ``==``, ``!=`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *12* | Logical AND | ``&&`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *13* | Logical OR | ``||`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *14* | Ternary operator | ``<conditional> ? <if-true> : <if-false>`` |
|
|
+ +-------------------------------------+--------------------------------------------+
|
|
| | Assignment operators | ``=``, ``|=``, ``^=``, ``&=``, ``<<=``, |
|
|
| | | ``>>=``, ``+=``, ``-=``, ``*=``, ``/=``, |
|
|
| | | ``%=`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
| *15* | Comma operator | ``,`` |
|
|
+------------+-------------------------------------+--------------------------------------------+
|
|
|
|
.. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
|
|
|
|
Global Variables
|
|
================
|
|
|
|
- ``abi.decode(bytes memory encodedData, (...)) returns (...)``: :ref:`ABI <ABI>`-decodes
|
|
the provided data. The types are given in parentheses as second argument.
|
|
Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
|
|
- ``abi.encode(...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes the given arguments
|
|
- ``abi.encodePacked(...) returns (bytes memory)``: Performs :ref:`packed encoding <abi_packed_mode>` of
|
|
the given arguments. Note that this encoding can be ambiguous!
|
|
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes
|
|
the given arguments starting from the second and prepends the given four-byte selector
|
|
- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent
|
|
to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)```
|
|
- ``block.coinbase`` (``address payable``): current block miner's address
|
|
- ``block.difficulty`` (``uint``): current block difficulty
|
|
- ``block.gaslimit`` (``uint``): current block gaslimit
|
|
- ``block.number`` (``uint``): current block number
|
|
- ``block.timestamp`` (``uint``): current block timestamp
|
|
- ``gasleft() returns (uint256)``: remaining gas
|
|
- ``msg.data`` (``bytes``): complete calldata
|
|
- ``msg.sender`` (``address payable``): sender of the message (current call)
|
|
- ``msg.value`` (``uint``): number of wei sent with the message
|
|
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
|
|
- ``tx.gasprice`` (``uint``): gas price of the transaction
|
|
- ``tx.origin`` (``address payable``): sender of the transaction (full call chain)
|
|
- ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error)
|
|
- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use
|
|
for malformed input or error in external component)
|
|
- ``require(bool condition, string memory message)``: abort execution and revert state changes if
|
|
condition is ``false`` (use for malformed input or error in external component). Also provide error message.
|
|
- ``revert()``: abort execution and revert state changes
|
|
- ``revert(string memory message)``: abort execution and revert state changes providing an explanatory string
|
|
- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks
|
|
- ``keccak256(bytes memory) returns (bytes32)``: compute the Keccak-256 hash of the input
|
|
- ``sha256(bytes memory) returns (bytes32)``: compute the SHA-256 hash of the input
|
|
- ``ripemd160(bytes memory) returns (bytes20)``: compute the RIPEMD-160 hash of the input
|
|
- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with
|
|
the public key from elliptic curve signature, return zero on error
|
|
- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with
|
|
arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
|
- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed
|
|
with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
|
- ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` or ``address payable``
|
|
- ``super``: the contract one level higher in the inheritance hierarchy
|
|
- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address
|
|
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
|
|
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`,
|
|
returns ``false`` on failure
|
|
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
|
|
- ``type(C).name`` (``string``): the name of the contract
|
|
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
|
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
|
|
|
.. note::
|
|
Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,
|
|
unless you know what you are doing.
|
|
|
|
Both the timestamp and the block hash can be influenced by miners to some degree.
|
|
Bad actors in the mining community can for example run a casino payout function on a chosen hash
|
|
and just retry a different hash if they did not receive any money.
|
|
|
|
The current block timestamp must be strictly larger than the timestamp of the last block,
|
|
but the only guarantee is that it will be somewhere between the timestamps of two
|
|
consecutive blocks in the canonical chain.
|
|
|
|
.. note::
|
|
The block hashes are not available for all blocks for scalability reasons.
|
|
You can only access the hashes of the most recent 256 blocks, all other
|
|
values will be zero.
|
|
|
|
.. note::
|
|
In version 0.5.0, the following aliases were removed: ``suicide`` as alias for ``selfdestruct``,
|
|
``msg.gas`` as alias for ``gasleft``, ``block.blockhash`` as alias for ``blockhash`` and
|
|
``sha3`` as alias for ``keccak256``.
|
|
|
|
.. index:: visibility, public, private, external, internal
|
|
|
|
Function Visibility Specifiers
|
|
==============================
|
|
|
|
::
|
|
|
|
function myFunction() <visibility specifier> returns (bool) {
|
|
return true;
|
|
}
|
|
|
|
- ``public``: visible externally and internally (creates a :ref:`getter function<getter-functions>` for storage/state variables)
|
|
- ``private``: only visible in the current contract
|
|
- ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``)
|
|
- ``internal``: only visible internally
|
|
|
|
|
|
.. index:: modifiers, pure, view, payable, constant, anonymous, indexed
|
|
|
|
Modifiers
|
|
=========
|
|
|
|
- ``pure`` for functions: Disallows modification or access of state.
|
|
- ``view`` for functions: Disallows modification of state.
|
|
- ``payable`` for functions: Allows them to receive Ether together with a call.
|
|
- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
|
|
- ``anonymous`` for events: Does not store event signature as topic.
|
|
- ``indexed`` for event parameters: Stores the parameter as topic.
|
|
- ``virtual`` for functions and modifiers: Allows the function's or modifier's
|
|
behaviour to be changed in derived contracts.
|
|
- ``override``: States that this function, modifier or public state variable changes
|
|
the behaviour of a function or modifier in a base contract.
|
|
|
|
Reserved Keywords
|
|
=================
|
|
|
|
These keywords are reserved in Solidity. They might become part of the syntax in the future:
|
|
|
|
``after``, ``alias``, ``apply``, ``auto``, ``case``, ``copyof``, ``default``,
|
|
``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
|
|
``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``,
|
|
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``,
|
|
``unchecked``.
|
|
|
|
Language Grammar
|
|
================
|
|
|
|
.. literalinclude:: grammar.txt
|
|
:language: none
|