mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into HEAD
This commit is contained in:
@@ -63,7 +63,7 @@ This section highlights changes that affect syntax and semantics.
|
||||
last one only works for value types). Change every ``keccak256(a, b, c)`` to
|
||||
``keccak256(abi.encodePacked(a, b, c))``. Even though it is not a breaking
|
||||
change, it is suggested that developers change
|
||||
``x.call(bytes4(keccak256("f(uint256)"), a, b)`` to
|
||||
``x.call(bytes4(keccak256("f(uint256)")), a, b)`` to
|
||||
``x.call(abi.encodeWithSignature("f(uint256)", a, b))``.
|
||||
|
||||
* Functions ``.call()``, ``.delegatecall()`` and ``.staticcall()`` now return
|
||||
@@ -455,7 +455,7 @@ New version:
|
||||
uint z = someInteger();
|
||||
x += z;
|
||||
// Throw is now disallowed.
|
||||
require(x > 100);
|
||||
require(x <= 100);
|
||||
int y = -3 >> 1;
|
||||
require(y == -2);
|
||||
do {
|
||||
|
||||
+34
-588
@@ -1,41 +1,20 @@
|
||||
#################
|
||||
Solidity Assembly
|
||||
#################
|
||||
.. _inline-assembly:
|
||||
|
||||
###############
|
||||
Inline Assembly
|
||||
###############
|
||||
|
||||
.. index:: ! assembly, ! asm, ! evmasm
|
||||
|
||||
Solidity defines an assembly language that you can use without Solidity and also
|
||||
as "inline assembly" inside Solidity source code. This guide starts with describing
|
||||
how to use inline assembly, how it differs from standalone assembly
|
||||
(sometimes also referred to by its proper name "Yul"), and
|
||||
specifies assembly itself.
|
||||
|
||||
.. _inline-assembly:
|
||||
|
||||
Inline Assembly
|
||||
===============
|
||||
|
||||
You can interleave Solidity statements with inline assembly in a language close
|
||||
to the one of the virtual machine. This gives you more fine-grained control,
|
||||
especially when you are enhancing the language by writing libraries.
|
||||
to the one of the Ethereum virtual machine. This gives you more fine-grained control,
|
||||
which is especially useful when you are enhancing the language by writing libraries.
|
||||
|
||||
As the EVM is a stack machine, it is often hard to address the correct stack slot
|
||||
and provide arguments to opcodes at the correct point on the stack. Solidity's inline
|
||||
assembly helps you do this, and with other issues that arise when writing manual assembly.
|
||||
The language used for inline assembly in Solidity is called `Yul <yul>`_
|
||||
and it is documented in its own section. This section will only cover
|
||||
how the inline assembly code can interface with the surrounding Solidity code.
|
||||
|
||||
For inline assembly, the stack is actually not visible at all, but if you look
|
||||
closer, there is always a very direct translation from inline assembly to
|
||||
the stack based EVM opcode stream.
|
||||
|
||||
Inline assembly has the following features:
|
||||
|
||||
* functional-style opcodes: ``mul(1, add(2, 3))``
|
||||
* assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)``
|
||||
* access to external variables: ``function f(uint x) public { assembly { x := sub(x, 1) } }``
|
||||
* loops: ``for { let i := 0 } lt(i, x) { i := add(i, 1) } { y := mul(2, y) }``
|
||||
* if statements: ``if slt(x, 0) { x := sub(0, x) }``
|
||||
* switch statements: ``switch x case 0 { y := mul(x, 2) } default { y := 0 }``
|
||||
* function calls: ``function f(x) -> y { switch x case 0 { y := 1 } default { y := mul(x, f(sub(x, 1))) } }``
|
||||
|
||||
.. warning::
|
||||
Inline assembly is a way to access the Ethereum Virtual Machine
|
||||
@@ -43,24 +22,14 @@ Inline assembly has the following features:
|
||||
features and checks of Solidity. You should only use it for
|
||||
tasks that need it, and only if you are confident with using it.
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
Assembly parses comments, literals and identifiers in the same way as Solidity, so you can use the
|
||||
usual ``//`` and ``/* */`` comments. There is one exception: Identifiers in inline assembly can contain
|
||||
``.``. Inline assembly is marked by ``assembly { ... }`` and inside
|
||||
these curly braces, you can use the following (see the later sections for more details):
|
||||
An inline assembly block is marked by ``assembly { ... }``, where the code inside
|
||||
the curly braces is code in the `Yul <yul>`_ language.
|
||||
|
||||
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
|
||||
- opcodes in functional style, e.g. ``add(1, mload(0))``
|
||||
- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned)
|
||||
- identifiers (assembly-local variables and externals if used as inline assembly), e.g. ``add(3, x)``, ``sstore(x_slot, 2)``
|
||||
- assignments, e.g. ``x := add(y, 3)``
|
||||
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
|
||||
The inline assembly code can access local Solidity variables as explained below.
|
||||
|
||||
Inline assembly manages local variables and control-flow. Because of that,
|
||||
opcodes that interfere with these features are not available. This includes
|
||||
the ``dup`` and ``swap`` instructions as well as ``jump`` instructions and labels.
|
||||
Different inline assembly blocks share no namespace, i.e. it is not possible
|
||||
to call a Yul function or access a Yul variable defined in a different inline assembly block.
|
||||
|
||||
Example
|
||||
-------
|
||||
@@ -146,238 +115,20 @@ efficient code, for example:
|
||||
}
|
||||
|
||||
|
||||
.. _opcodes:
|
||||
|
||||
Opcodes
|
||||
-------
|
||||
|
||||
This document does not want to be a full description of the Ethereum virtual machine, but the
|
||||
following list can be used as a quick reference of its opcodes.
|
||||
|
||||
If an opcode takes arguments, they are given in parentheses.
|
||||
Opcodes marked with ``-`` do not return a result,
|
||||
those marked with ``*`` are special in a certain way and all others return exactly one value.
|
||||
Opcodes marked with ``F``, ``H``, ``B``, ``C`` or ``I`` are present since Frontier, Homestead,
|
||||
Byzantium, Constantinople or Istanbul, respectively.
|
||||
|
||||
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
|
||||
but not including position ``b`` and ``storage[p]`` signifies the storage contents at slot ``p``.
|
||||
|
||||
In the grammar, opcodes are represented as pre-defined identifiers ("built-in functions").
|
||||
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| Instruction | | | Explanation |
|
||||
+=========================+=====+===+=================================================================+
|
||||
| stop() + `-` | F | stop execution, identical to return(0, 0) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| add(x, y) | | F | x + y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| sub(x, y) | | F | x - y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mul(x, y) | | F | x * y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| div(x, y) | | F | x / y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| sdiv(x, y) | | F | x / y, for signed numbers in two's complement |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mod(x, y) | | F | x % y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| smod(x, y) | | F | x % y, for signed numbers in two's complement |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| exp(x, y) | | F | x to the power of y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| not(x) | | F | ~x, every bit of x is negated |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| lt(x, y) | | F | 1 if x < y, 0 otherwise |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| gt(x, y) | | F | 1 if x > y, 0 otherwise |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| slt(x, y) | | F | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| sgt(x, y) | | F | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| eq(x, y) | | F | 1 if x == y, 0 otherwise |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| iszero(x) | | F | 1 if x == 0, 0 otherwise |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| and(x, y) | | F | bitwise "and" of x and y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| or(x, y) | | F | bitwise "or" of x and y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| xor(x, y) | | F | bitwise "xor" of x and y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| shl(x, y) | | C | logical shift left y by x bits |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| shr(x, y) | | C | logical shift right y by x bits |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| sar(x, y) | | C | signed arithmetic shift right y by x bits |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetic |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetic |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| keccak256(p, n) | | F | keccak(mem[p...(p+n))) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| pc() | | F | current position in code |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| pop(x) | `-` | F | discard value x |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mload(p) | | F | mem[p...(p+32)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mstore(p, v) | `-` | F | mem[p...(p+32)) := v |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| sload(p) | | F | storage[p] |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| sstore(p, v) | `-` | F | storage[p] := v |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| msize() | | F | size of memory, i.e. largest accessed memory index |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| gas() | | F | gas still available to execution |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| address() | | F | address of the current contract / execution context |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| balance(a) | | F | wei balance at address a |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| selfbalance() | | I | equivalent to balance(address()), but cheaper |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| caller() | | F | call sender (excluding ``delegatecall``) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| callvalue() | | F | wei sent together with the current call |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| calldataload(p) | | F | call data starting from position p (32 bytes) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| calldatasize() | | F | size of call data in bytes |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| calldatacopy(t, f, s) | `-` | F | copy s bytes from calldata at position f to mem at position t |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| codesize() | | F | size of the code of the current contract / execution context |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| codecopy(t, f, s) | `-` | F | copy s bytes from code at position f to mem at position t |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| extcodesize(a) | | F | size of the code at address a |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| extcodecopy(a, t, f, s) | `-` | F | like codecopy(t, f, s) but take code at address a |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| returndatasize() | | B | size of the last returndata |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| extcodehash(a) | | C | code hash of address a |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| create(v, p, n) | | F | create new contract with code mem[p...(p+n)) and send v wei |
|
||||
| | | | and return the new address |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address |
|
||||
| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) |
|
||||
| | | | and send v wei and return the new address, where ``0xff`` is a |
|
||||
| | | | 1 byte value, ``this`` is the current contract's address |
|
||||
| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) |
|
||||
| insize, out, outsize) | | | providing g gas and v wei and output area |
|
||||
| | | | mem[out...(out+outsize)) returning 0 on error (eg. out of gas) |
|
||||
| | | | and 1 on success |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay |
|
||||
| insize, out, outsize) | | | in the context of the current contract otherwise |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` |
|
||||
| insize, out, outsize) | | | and ``callvalue`` |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do |
|
||||
| insize, out, outsize) | | | not allow state modifications |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| return(p, s) | `-` | F | end execution, return data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| invalid() | `-` | F | end execution with invalid instruction |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log0(p, s) | `-` | F | log without topics and data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p...(p+s)) |
|
||||
| t4) | | | |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| chainid() | | I | ID of the executing chain (EIP 1344) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| origin() | | F | transaction sender |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| gasprice() | | F | gas price of the transaction |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| coinbase() | | F | current mining beneficiary |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| timestamp() | | F | timestamp of the current block in seconds since the epoch |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| number() | | F | current block number |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| difficulty() | | F | difficulty of the current block |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| gaslimit() | | F | block gas limit of the current block |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
|
||||
Literals
|
||||
--------
|
||||
|
||||
You can use integer constants by typing them in decimal or hexadecimal notation and an
|
||||
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
|
||||
to add 2 and 3 resulting in 5 and then computes the bitwise ``AND`` with the string "abc".
|
||||
The final value is assigned to a local variable called ``x``.
|
||||
Strings are stored left-aligned and cannot be longer than 32 bytes.
|
||||
|
||||
.. code::
|
||||
|
||||
assembly { let x := and("abc", add(3, 2)) }
|
||||
|
||||
|
||||
Functional Style
|
||||
-----------------
|
||||
|
||||
For a sequence of opcodes, it is often hard to see what the actual
|
||||
arguments for certain opcodes are. In the following example,
|
||||
``3`` is added to the contents in memory at position ``0x80``.
|
||||
|
||||
.. code::
|
||||
|
||||
3 0x80 mload add 0x80 mstore
|
||||
|
||||
Solidity inline assembly has a "functional style" notation where the same code
|
||||
would be written as follows:
|
||||
|
||||
.. code::
|
||||
|
||||
mstore(0x80, add(mload(0x80), 3))
|
||||
|
||||
If you read the code from right to left, you end up with exactly the same
|
||||
sequence of constants and opcodes, but it is much clearer where the
|
||||
values end up.
|
||||
|
||||
If you care about the exact stack layout, just note that the
|
||||
syntactically first argument for a function or opcode will be put at the
|
||||
top of the stack.
|
||||
|
||||
Access to External Variables, Functions and Libraries
|
||||
-----------------------------------------------------
|
||||
|
||||
You can access Solidity variables and other identifiers by using their name.
|
||||
For variables stored in the memory data location, this pushes the address, and not the value
|
||||
onto the stack. Variables stored in the storage data location are different, as they might not
|
||||
occupy a full storage slot, so their "address" is composed of a slot and a byte-offset
|
||||
|
||||
Local variables of value type are directly usable in inline assembly.
|
||||
|
||||
Local variables that refer to memory or calldata evaluate to the
|
||||
address of the variable in memory, resp. calldata, not the value itself.
|
||||
|
||||
For local storage variables or state variables, a single Yul identifier
|
||||
is not sufficient, since they do not necessarily occupy a single full storage slot.
|
||||
Therefore, their "address" is composed of a slot and a byte-offset
|
||||
inside that slot. To retrieve the slot pointed to by the variable ``x``, you
|
||||
use ``x_slot``, and to retrieve the byte-offset you use ``x_offset``.
|
||||
|
||||
@@ -391,7 +142,9 @@ Local Solidity variables are available for assignments, for example:
|
||||
uint b;
|
||||
function f(uint x) public view returns (uint r) {
|
||||
assembly {
|
||||
r := mul(x, sload(b_slot)) // ignore the offset, we know it is zero
|
||||
// We ignore the storage slot offset, we know it is zero
|
||||
// in this special case.
|
||||
r := mul(x, sload(b_slot))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -407,177 +160,24 @@ Local Solidity variables are available for assignments, for example:
|
||||
To clean signed types, you can use the ``signextend`` opcode:
|
||||
``assembly { signextend(<num_bytes_of_x_minus_one>, x) }``
|
||||
|
||||
Declaring Assembly-Local Variables
|
||||
----------------------------------
|
||||
|
||||
You can use the ``let`` keyword to declare variables that are only visible in
|
||||
inline assembly and actually only in the current ``{...}``-block. What happens
|
||||
is that the ``let`` instruction will create a new stack slot that is reserved
|
||||
for the variable and automatically removed again when the end of the block
|
||||
is reached. You need to provide an initial value for the variable which can
|
||||
be just ``0``, but it can also be a complex functional-style expression.
|
||||
|
||||
Since 0.6.0 the name of a declared variable may not end in ``_offset`` or ``_slot``
|
||||
Since Solidity 0.6.0 the name of a inline assembly variable may not end in ``_offset`` or ``_slot``
|
||||
and it may not shadow any declaration visible in the scope of the inline assembly block
|
||||
(including variable, contract and function declarations). Similarly, if the name of a declared
|
||||
variable contains a dot ``.``, the prefix up to the ``.`` may not conflict with any
|
||||
declaration visible in the scope of the inline assembly block.
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
contract C {
|
||||
function f(uint x) public view returns (uint b) {
|
||||
assembly {
|
||||
let v := add(x, 1)
|
||||
mstore(0x80, v)
|
||||
{
|
||||
let y := add(sload(v), 1)
|
||||
b := y
|
||||
} // y is "deallocated" here
|
||||
b := add(b, v)
|
||||
} // v is "deallocated" here
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Assignments
|
||||
-----------
|
||||
|
||||
Assignments are possible to assembly-local variables and to function-local
|
||||
variables. Take care that when you assign to variables that point to
|
||||
memory or storage, you will only change the pointer and not the data.
|
||||
|
||||
Variables can only be assigned expressions that result in exactly one value.
|
||||
If you want to assign the values returned from a function that has
|
||||
multiple return parameters, you have to provide multiple variables.
|
||||
You can assign to the ``_slot`` part of a local storage variable pointer.
|
||||
For these (structs, arrays or mappings), the ``_offset`` part is always zero.
|
||||
It is not possible to assign to the ``_slot`` or ``_offset`` part of a state variable,
|
||||
though.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
let v := 0
|
||||
let g := add(v, 2)
|
||||
function f() -> a, b { }
|
||||
let c, d := f()
|
||||
}
|
||||
|
||||
If
|
||||
--
|
||||
|
||||
The if statement can be used for conditionally executing code.
|
||||
There is no "else" part, consider using "switch" (see below) if
|
||||
you need multiple alternatives.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
if eq(value, 0) { revert(0, 0) }
|
||||
}
|
||||
|
||||
The curly braces for the body are required.
|
||||
|
||||
Switch
|
||||
------
|
||||
|
||||
You can use a switch statement as a very basic version of "if/else".
|
||||
It takes the value of an expression and compares it to several constants.
|
||||
The branch corresponding to the matching constant is taken. Contrary to the
|
||||
error-prone behaviour of some programming languages, control flow does
|
||||
not continue from one case to the next. There can be a fallback or default
|
||||
case called ``default``.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
let x := 0
|
||||
switch calldataload(4)
|
||||
case 0 {
|
||||
x := calldataload(0x24)
|
||||
}
|
||||
default {
|
||||
x := calldataload(0x44)
|
||||
}
|
||||
sstore(0, div(x, 2))
|
||||
}
|
||||
|
||||
The list of cases does not require curly braces, but the body of a
|
||||
case does require them.
|
||||
|
||||
Loops
|
||||
-----
|
||||
|
||||
Assembly supports a simple for-style loop. For-style loops have
|
||||
a header containing an initializing part, a condition and a post-iteration
|
||||
part. The condition has to be a functional-style expression, while
|
||||
the other two are blocks. If the initializing part
|
||||
declares any variables, the scope of these variables is extended into the
|
||||
body (including the condition and the post-iteration part).
|
||||
|
||||
The ``break`` and ``continue`` statements can be used to exit the loop
|
||||
or skip to the post-part, respectively.
|
||||
|
||||
The following example computes the sum of an area in memory.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
let x := 0
|
||||
for { let i := 0 } lt(i, 0x100) { i := add(i, 0x20) } {
|
||||
x := add(x, mload(i))
|
||||
}
|
||||
}
|
||||
|
||||
For loops can also be written so that they behave like while loops:
|
||||
Simply leave the initialization and post-iteration parts empty.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
let x := 0
|
||||
let i := 0
|
||||
for { } lt(i, 0x100) { } { // while(i < 0x100)
|
||||
x := add(x, mload(i))
|
||||
i := add(i, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
Assembly allows the definition of low-level functions. These take their
|
||||
arguments (and a return PC) from the stack and also put the results onto the
|
||||
stack. Calling a function looks the same way as executing a functional-style
|
||||
opcode.
|
||||
|
||||
Functions can be defined anywhere and are visible in the block they are
|
||||
declared in. Inside a function, you cannot access local variables
|
||||
defined outside of that function.
|
||||
|
||||
If you call a function that returns multiple values, you have to assign
|
||||
them to a tuple using ``a, b := f(x)`` or ``let a, b := f(x)``.
|
||||
|
||||
The ``leave`` statement can be used to exit the current function. It
|
||||
works like the ``return`` statement in other languages just that it does
|
||||
not take a value to return, it just exits the functions and the function
|
||||
will return whatever values are currently assigned to the return variable(s).
|
||||
|
||||
The following example implements the power function by square-and-multiply.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
function power(base, exponent) -> result {
|
||||
switch exponent
|
||||
case 0 { result := 1 }
|
||||
case 1 { result := base }
|
||||
default {
|
||||
result := power(mul(base, base), div(exponent, 2))
|
||||
switch mod(exponent, 2)
|
||||
case 1 { result := mul(base, result) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Things to Avoid
|
||||
---------------
|
||||
@@ -593,7 +193,8 @@ Conventions in Solidity
|
||||
-----------------------
|
||||
|
||||
In contrast to EVM assembly, Solidity has types which are narrower than 256 bits,
|
||||
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that types can be shorter than 256
|
||||
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that
|
||||
types can be shorter than 256
|
||||
bits, and the higher-order bits are cleaned when necessary,
|
||||
i.e., shortly before they are written to memory or before comparisons are performed.
|
||||
This means that if you access such a variable
|
||||
@@ -629,158 +230,3 @@ first slot of the array and followed by the array elements.
|
||||
Statically-sized memory arrays do not have a length field, but it might be added later
|
||||
to allow better convertibility between statically- and dynamically-sized arrays, so
|
||||
do not rely on this.
|
||||
|
||||
|
||||
Standalone Assembly
|
||||
===================
|
||||
|
||||
The assembly language described as inline assembly above can also be used
|
||||
standalone and in fact, the plan is to use it as an intermediate language
|
||||
for the Solidity compiler. In this form, it tries to achieve several goals:
|
||||
|
||||
1. Programs written in it should be readable, even if the code is generated by a compiler from Solidity.
|
||||
2. The translation from assembly to bytecode should contain as few "surprises" as possible.
|
||||
3. Control flow should be easy to detect to help in formal verification and optimization.
|
||||
|
||||
In order to achieve the first and last goal, assembly provides high-level constructs
|
||||
like ``for`` loops, ``if`` and ``switch`` statements and function calls. It should be possible
|
||||
to write assembly programs that do not make use of explicit ``SWAP``, ``DUP``,
|
||||
``JUMP`` and ``JUMPI`` statements, because the first two obfuscate the data flow
|
||||
and the last two obfuscate control flow. Furthermore, functional statements of
|
||||
the form ``mul(add(x, y), 7)`` are preferred over pure opcode statements like
|
||||
``7 y x add mul`` because in the first form, it is much easier to see which
|
||||
operand is used for which opcode.
|
||||
|
||||
The second goal is achieved by compiling the
|
||||
higher level constructs to bytecode in a very regular way.
|
||||
The only non-local operation performed
|
||||
by the assembler is name lookup of user-defined identifiers (functions, variables, ...),
|
||||
which follow very simple and regular scoping rules and cleanup of local variables from the stack.
|
||||
|
||||
Scoping: An identifier that is declared (label, variable, function, assembly)
|
||||
is only visible in the block where it was declared (including nested blocks
|
||||
inside the current block). It is not legal to access local variables across
|
||||
function borders, even if they would be in scope. Shadowing is not allowed.
|
||||
Local variables cannot be accessed before they were declared, but
|
||||
functions and assemblies can. Assemblies are special blocks that are used
|
||||
for e.g. returning runtime code or creating contracts. No identifier from an
|
||||
outer assembly is visible in a sub-assembly.
|
||||
|
||||
If control flow passes over the end of a block, pop instructions are inserted
|
||||
that match the number of local variables declared in that block.
|
||||
Whenever a local variable is referenced, the code generator needs
|
||||
to know its current relative position in the stack and thus it needs to
|
||||
keep track of the current so-called stack height. Since all local variables
|
||||
are removed at the end of a block, the stack height before and after the block
|
||||
should be the same. If this is not the case, compilation fails.
|
||||
|
||||
Using ``switch``, ``for`` and functions, it should be possible to write
|
||||
complex code without using ``jump`` or ``jumpi`` manually. This makes it much
|
||||
easier to analyze the control flow, which allows for improved formal
|
||||
verification and optimization.
|
||||
|
||||
Furthermore, if manual jumps are allowed, computing the stack height is rather complicated.
|
||||
The position of all local variables on the stack needs to be known, otherwise
|
||||
neither references to local variables nor removing local variables automatically
|
||||
from the stack at the end of a block will work properly.
|
||||
|
||||
Example:
|
||||
|
||||
We will follow an example compilation from Solidity to assembly.
|
||||
We consider the runtime bytecode of the following Solidity program::
|
||||
|
||||
pragma solidity >=0.4.16 <0.8.0;
|
||||
|
||||
|
||||
contract C {
|
||||
function f(uint x) public pure returns (uint y) {
|
||||
y = 1;
|
||||
for (uint i = 0; i < x; i++)
|
||||
y = 2 * y;
|
||||
}
|
||||
}
|
||||
|
||||
The following assembly will be generated::
|
||||
|
||||
{
|
||||
mstore(0x40, 0x80) // store the "free memory pointer"
|
||||
// function dispatcher
|
||||
switch div(calldataload(0), exp(2, 226))
|
||||
case 0xb3de648b {
|
||||
let r := f(calldataload(4))
|
||||
let ret := $allocate(0x20)
|
||||
mstore(ret, r)
|
||||
return(ret, 0x20)
|
||||
}
|
||||
default { revert(0, 0) }
|
||||
// memory allocator
|
||||
function $allocate(size) -> pos {
|
||||
pos := mload(0x40)
|
||||
mstore(0x40, add(pos, size))
|
||||
}
|
||||
// the contract function
|
||||
function f(x) -> y {
|
||||
y := 1
|
||||
for { let i := 0 } lt(i, x) { i := add(i, 1) } {
|
||||
y := mul(2, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Assembly Grammar
|
||||
----------------
|
||||
|
||||
The tasks of the parser are the following:
|
||||
|
||||
- Turn the byte stream into a token stream, discarding C++-style comments
|
||||
(a special comment exists for source references, but we will not explain it here).
|
||||
- Turn the token stream into an AST according to the grammar below
|
||||
- Register identifiers with the block they are defined in (annotation to the
|
||||
AST node) and note from which point on, variables can be accessed.
|
||||
|
||||
The assembly lexer follows the one defined by Solidity itself.
|
||||
|
||||
Whitespace is used to delimit tokens and it consists of the characters
|
||||
Space, Tab and Linefeed. Comments are regular JavaScript/C++ comments and
|
||||
are interpreted in the same way as Whitespace.
|
||||
|
||||
Grammar::
|
||||
|
||||
AssemblyBlock = '{' AssemblyItem* '}'
|
||||
AssemblyItem =
|
||||
Identifier |
|
||||
AssemblyBlock |
|
||||
AssemblyExpression |
|
||||
AssemblyLocalDefinition |
|
||||
AssemblyAssignment |
|
||||
AssemblyIf |
|
||||
AssemblySwitch |
|
||||
AssemblyFunctionDefinition |
|
||||
AssemblyFor |
|
||||
'break' |
|
||||
'continue' |
|
||||
'leave' |
|
||||
SubAssembly
|
||||
AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral
|
||||
AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral
|
||||
Identifier = [a-zA-Z_$] [a-zA-Z_0-9.]*
|
||||
AssemblyCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')'
|
||||
AssemblyLocalDefinition = 'let' IdentifierOrList ( ':=' AssemblyExpression )?
|
||||
AssemblyAssignment = IdentifierOrList ':=' AssemblyExpression
|
||||
IdentifierOrList = Identifier | '(' IdentifierList ')'
|
||||
IdentifierList = Identifier ( ',' Identifier)*
|
||||
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
|
||||
AssemblySwitch = 'switch' AssemblyExpression AssemblyCase*
|
||||
( 'default' AssemblyBlock )?
|
||||
AssemblyCase = 'case' AssemblyExpression AssemblyBlock
|
||||
AssemblyFunctionDefinition = 'function' Identifier '(' IdentifierList? ')'
|
||||
( '->' '(' IdentifierList ')' )? AssemblyBlock
|
||||
AssemblyFor = 'for' ( AssemblyBlock | AssemblyExpression )
|
||||
AssemblyExpression ( AssemblyBlock | AssemblyExpression ) AssemblyBlock
|
||||
SubAssembly = 'assembly' Identifier AssemblyBlock
|
||||
NumberLiteral = HexNumber | DecimalNumber
|
||||
HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
|
||||
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
|
||||
HexNumber = '0x' [0-9a-fA-F]+
|
||||
DecimalNumber = [0-9]+
|
||||
|
||||
@@ -880,5 +880,13 @@
|
||||
"0.6.1": {
|
||||
"bugs": [],
|
||||
"released": "2020-01-02"
|
||||
},
|
||||
"0.6.2": {
|
||||
"bugs": [],
|
||||
"released": "2020-01-27"
|
||||
},
|
||||
"0.6.3": {
|
||||
"bugs": [],
|
||||
"released": "2020-02-18"
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -17,6 +17,8 @@ import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
from pygments_lexer_solidity import SolidityLexer
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
@@ -24,7 +26,6 @@ import re
|
||||
def setup(sphinx):
|
||||
thisdir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, thisdir + '/utils')
|
||||
from pygments_lexer_solidity import SolidityLexer
|
||||
sphinx.add_lexer('Solidity', SolidityLexer())
|
||||
|
||||
sphinx.add_stylesheet('css/custom.css')
|
||||
@@ -53,7 +54,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'Solidity'
|
||||
copyright = '2016-2019, Ethereum'
|
||||
copyright = '2016-2020, Ethereum'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
||||
@@ -29,7 +29,7 @@ value types and strings.
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
|
||||
contract C {
|
||||
uint constant x = 32**22 + 8;
|
||||
string constant text = "abc";
|
||||
bytes32 constant myHash = keccak256("abc");
|
||||
uint constant X = 32**22 + 8;
|
||||
string constant TEXT = "abc";
|
||||
bytes32 constant MY_HASH = keccak256("abc");
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ operations as long as there is enough gas passed on to it.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
pragma solidity >0.6.1 <0.8.0;
|
||||
|
||||
contract Test {
|
||||
// This function is called for all messages sent to
|
||||
@@ -382,7 +382,7 @@ operations as long as there is enough gas passed on to it.
|
||||
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
|
||||
require(success);
|
||||
// results in test.x becoming == 1 and test.y becoming 0.
|
||||
(success,) = address(test).call.value(1)(abi.encodeWithSignature("nonExistingFunction()"));
|
||||
(success,) = address(test).call{value: 1}(abi.encodeWithSignature("nonExistingFunction()"));
|
||||
require(success);
|
||||
// results in test.x becoming == 1 and test.y becoming 1.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Interfaces
|
||||
|
||||
Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions:
|
||||
|
||||
- They cannot inherit other contracts or interfaces.
|
||||
- They cannot inherit from other contracts, but they can inherit from other interfaces.
|
||||
- All declared functions must be external.
|
||||
- They cannot declare a constructor.
|
||||
- They cannot declare state variables.
|
||||
@@ -37,10 +37,31 @@ they can be overridden. This does not automatically mean that an overriding func
|
||||
can be overridden again - this is only possible if the overriding
|
||||
function is marked ``virtual``.
|
||||
|
||||
Interfaces can inherit from other interfaces. This has the same rules as normal
|
||||
inheritance.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.6.1 <0.8.0;
|
||||
|
||||
interface ParentA {
|
||||
function test() external returns (uint256);
|
||||
}
|
||||
|
||||
interface ParentB {
|
||||
function test() external returns (uint256);
|
||||
}
|
||||
|
||||
interface SubInterface is ParentA, ParentB {
|
||||
// Must redefine test in order to assert that the parent
|
||||
// meanings are compatible.
|
||||
function test() external override(ParentA, ParentB) returns (uint256);
|
||||
}
|
||||
|
||||
Types defined inside interfaces and other contract-like structures
|
||||
can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``.
|
||||
|
||||
.. warning:
|
||||
|
||||
Interfaces have supported ``enum`` types since :doc:`Solidity version 0.5.0 <050-breaking-changes>`, make
|
||||
sure the pragma version specifies this version as a minimum.
|
||||
sure the pragma version specifies this version as a minimum.
|
||||
|
||||
@@ -328,7 +328,7 @@ Whiskers
|
||||
compiler in various places to aid readability, and thus maintainability and verifiability, of the code.
|
||||
|
||||
The syntax comes with a substantial difference to Mustache. The template markers ``{{`` and ``}}`` are
|
||||
replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`inline-assembly`
|
||||
replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`yul`
|
||||
(The symbols ``<`` and ``>`` are invalid in inline assembly, while ``{`` and ``}`` are used to delimit blocks).
|
||||
Another limitation is that lists are only resolved one depth and they do not recurse. This may change in the future.
|
||||
|
||||
|
||||
+90
-20
@@ -75,9 +75,10 @@ all function arguments have to be copied to memory.
|
||||
it is a message call as part of the overall transaction.
|
||||
|
||||
When calling functions of other contracts, you can specify the amount of Wei or
|
||||
gas sent with the call with the special options ``.value()`` and ``.gas()``,
|
||||
respectively. Any Wei you send to the contract is added to the total balance
|
||||
of the contract:
|
||||
gas sent with the call with the special options ``{value: 10, gas: 10000}``.
|
||||
Note that it is discouraged to specify gas values explicitly, since the gas costs
|
||||
of opcodes can change in the future. Any Wei you send to the contract is added
|
||||
to the total balance of that contract:
|
||||
|
||||
::
|
||||
|
||||
@@ -90,14 +91,14 @@ of the contract:
|
||||
contract Consumer {
|
||||
InfoFeed feed;
|
||||
function setFeed(InfoFeed addr) public { feed = addr; }
|
||||
function callFeed() public { feed.info.value(10).gas(800)(); }
|
||||
function callFeed() public { feed.info{value: 10, gas: 800}(); }
|
||||
}
|
||||
|
||||
You need to use the modifier ``payable`` with the ``info`` function because
|
||||
otherwise, the ``.value()`` option would not be available.
|
||||
otherwise, the ``value`` option would not be available.
|
||||
|
||||
.. warning::
|
||||
Be careful that ``feed.info.value(10).gas(800)`` only locally sets the
|
||||
Be careful that ``feed.info{value: 10, gas: 800}`` only locally sets the
|
||||
``value`` and amount of ``gas`` sent with the function call, and the
|
||||
parentheses at the end perform the actual call. So in this case, the
|
||||
function is not called and the ``value`` and ``gas`` settings are lost.
|
||||
@@ -121,6 +122,11 @@ throws an exception or goes out of gas.
|
||||
external functions happen after any changes to state variables in your contract
|
||||
so your contract is not vulnerable to a reentrancy exploit.
|
||||
|
||||
.. note::
|
||||
Before Solidity 0.6.2, the recommended way to specify the value and gas
|
||||
was to use ``f.value(x).gas(g)()``. This is still possible but deprecated
|
||||
and will be removed with Solidity 0.7.0.
|
||||
|
||||
Named Calls and Anonymous Function Parameters
|
||||
---------------------------------------------
|
||||
|
||||
@@ -196,17 +202,81 @@ is compiled so recursive creation-dependencies are not possible.
|
||||
|
||||
function createAndEndowD(uint arg, uint amount) public payable {
|
||||
// Send ether along with the creation
|
||||
D newD = (new D).value(amount)(arg);
|
||||
D newD = new D{value: amount}(arg);
|
||||
newD.x();
|
||||
}
|
||||
}
|
||||
|
||||
As seen in the example, it is possible to send Ether while creating
|
||||
an instance of ``D`` using the ``.value()`` option, but it is not possible
|
||||
an instance of ``D`` using the ``value`` option, but it is not possible
|
||||
to limit the amount of gas.
|
||||
If the creation fails (due to out-of-stack, not enough balance or other problems),
|
||||
an exception is thrown.
|
||||
|
||||
Salted contract creations / create2
|
||||
-----------------------------------
|
||||
|
||||
When creating a contract, the address of the contract is computed from
|
||||
the address of the creating contract and a counter that is increased with
|
||||
each contract creation.
|
||||
|
||||
If you specify the option ``salt`` (a bytes32 value), then contract creation will
|
||||
use a different mechanism to come up with the address of the new contract:
|
||||
|
||||
It will compute the address from the address of the creating contract,
|
||||
the given salt value, the (creation) bytecode of the created contract and the constructor
|
||||
arguments.
|
||||
|
||||
In particular, the counter ("nonce") is not used. This allows for more flexibility
|
||||
in creating contracts: You are able to derive the address of the
|
||||
new contract before it is created. Furthermore, you can rely on this address
|
||||
also in case the creating
|
||||
contracts creates other contracts in the meantime.
|
||||
|
||||
The main use-case here is contracts that act as judges for off-chain interactions,
|
||||
which only need to be created if there is a dispute.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.6.1 <0.8.0;
|
||||
|
||||
contract D {
|
||||
uint public x;
|
||||
constructor(uint a) public {
|
||||
x = a;
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
function createDSalted(bytes32 salt, uint arg) public {
|
||||
/// This complicated expression just tells you how the address
|
||||
/// can be pre-computed. It is just there for illustration.
|
||||
/// You actually only need ``new D{salt: salt}(arg)``.
|
||||
address predictedAddress = address(bytes20(keccak256(abi.encodePacked(
|
||||
byte(0xff),
|
||||
address(this),
|
||||
salt,
|
||||
keccak256(abi.encodePacked(
|
||||
type(D).creationCode,
|
||||
arg
|
||||
))
|
||||
))));
|
||||
|
||||
D d = new D{salt: salt}(arg);
|
||||
require(address(d) == predictedAddress);
|
||||
}
|
||||
}
|
||||
|
||||
.. warning::
|
||||
There are some peculiarities in relation to salted creation. A contract can be
|
||||
re-created at the same address after having been destroyed. Yet, it is possible
|
||||
for that newly created contract to have a different deployed bytecode even
|
||||
though the creation bytecode has been the same (which is a requirement because
|
||||
otherwise the address would change). This is due to the fact that the compiler
|
||||
can query external state that might have changed between the two creations
|
||||
and incorporate that into the deployed bytecode before it is stored.
|
||||
|
||||
|
||||
Order of Evaluation of Expressions
|
||||
==================================
|
||||
|
||||
@@ -273,18 +343,8 @@ i.e. the following is not valid: ``(x, uint y) = (1, 2);``
|
||||
Complications for Arrays and Structs
|
||||
------------------------------------
|
||||
|
||||
The semantics of assignments are a bit more complicated for
|
||||
non-value types like arrays and structs.
|
||||
Assigning *to* a state variable always creates an independent
|
||||
copy. On the other hand, assigning to a local variable creates
|
||||
an independent copy only for elementary types, i.e. static
|
||||
types that fit into 32 bytes. If structs or arrays (including
|
||||
``bytes`` and ``string``) are assigned from a state variable
|
||||
to a local variable, the local variable holds a reference to
|
||||
the original state variable. A second assignment to the local
|
||||
variable does not modify the state but only changes the
|
||||
reference. Assignments to members (or elements) of the local
|
||||
variable *do* change the state.
|
||||
The semantics of assignments are more complicated for non-value types like arrays and structs,
|
||||
including ``bytes`` and ``string``, see :ref:`Data location and assignment behaviour <data-location-assignment>` for details.
|
||||
|
||||
In the example below the call to ``g(x)`` has no effect on ``x`` because it creates
|
||||
an independent copy of the storage value in memory. However, ``h(x)`` successfully modifies ``x``
|
||||
@@ -647,3 +707,13 @@ in scope in the block that follows.
|
||||
in a catch block or the execution of the try/catch statement itself
|
||||
reverts (for example due to decoding failures as noted above or
|
||||
due to not providing a low-level catch clause).
|
||||
|
||||
.. note::
|
||||
The reason behind a failed call can be manifold. Do not assume that
|
||||
the error message is coming directly from the called contract:
|
||||
The error might have happened deeper down in the call chain and the
|
||||
called contract just forwarded it. Also, it could be due to an
|
||||
out-of-gas situation and not a deliberate error condition:
|
||||
The caller always retains 63/64th of the gas in a call and thus
|
||||
even if the called contract goes out of gas, the caller still
|
||||
has some gas left.
|
||||
+2
-1
@@ -59,7 +59,7 @@ TypeName = ElementaryTypeName
|
||||
|
||||
UserDefinedTypeName = Identifier ( '.' Identifier )*
|
||||
|
||||
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
|
||||
Mapping = 'mapping' '(' ( ElementaryTypeName | UserDefinedTypeName ) '=>' TypeName ')'
|
||||
ArrayTypeName = TypeName '[' Expression? ']'
|
||||
FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )*
|
||||
( 'returns' FunctionTypeParameterList )?
|
||||
@@ -96,6 +96,7 @@ Expression
|
||||
| IndexRangeAccess
|
||||
| MemberAccess
|
||||
| FunctionCall
|
||||
| Expression '{' NameValueList '}'
|
||||
| '(' Expression ')'
|
||||
| ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression
|
||||
| Expression '**' Expression
|
||||
|
||||
@@ -104,4 +104,3 @@ Contents
|
||||
common-patterns.rst
|
||||
bugs.rst
|
||||
contributing.rst
|
||||
lll.rst
|
||||
|
||||
@@ -174,7 +174,8 @@ Install it using ``brew``:
|
||||
# eg. Install 0.4.8
|
||||
brew install https://raw.githubusercontent.com/ethereum/homebrew-ethereum/77cce03da9f289e5a3ffe579840d3c5dc0a62717/solidity.rb
|
||||
|
||||
Gentoo Linux also provides a solidity package that can be installed using ``emerge``:
|
||||
Gentoo Linux has an `Ethereum overlay <https://overlays.gentoo.org/#ethereum>`_ that contains a solidity package.
|
||||
After the overlay is setup, ``solc`` can be installed in x86_64 architectures by:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
###
|
||||
LLL
|
||||
###
|
||||
|
||||
.. _lll:
|
||||
|
||||
LLL is a low-level language for the EVM with an s-expressions syntax.
|
||||
|
||||
The Solidity repository contains an LLL compiler, which shares the assembler subsystem with Solidity.
|
||||
However, apart from maintaining that it still compiles, no other improvements are made to it.
|
||||
|
||||
It is not built unless specifically requested:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cmake -DLLL=ON ..
|
||||
$ cmake --build .
|
||||
|
||||
.. warning::
|
||||
|
||||
The LLL codebase is deprecated and will be removed from the Solidity repository in the future.
|
||||
+1
-1
@@ -142,7 +142,7 @@ to the end of the deployed bytecode::
|
||||
0xa2
|
||||
0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash>
|
||||
0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding>
|
||||
0x00 0x32
|
||||
0x00 0x33
|
||||
|
||||
So in order to retrieve the data, the end of the deployed bytecode can be checked
|
||||
to match that pattern and use the IPFS hash to retrieve the file.
|
||||
|
||||
@@ -89,7 +89,7 @@ as it uses ``call`` which forwards all remaining gas by default:
|
||||
mapping(address => uint) shares;
|
||||
/// Withdraw your share.
|
||||
function withdraw() public {
|
||||
(bool success,) = msg.sender.call.value(shares[msg.sender])("");
|
||||
(bool success,) = msg.sender.call{value: shares[msg.sender]}("");
|
||||
if (success)
|
||||
shares[msg.sender] = 0;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ Sending and Receiving Ether
|
||||
(for example in the "details" section in Remix).
|
||||
|
||||
- There is a way to forward more gas to the receiving contract using
|
||||
``addr.call.value(x)("")``. This is essentially the same as ``addr.transfer(x)``,
|
||||
``addr.call{value: x}("")``. This is essentially the same as ``addr.transfer(x)``,
|
||||
only that it forwards all remaining gas and opens up the ability for the
|
||||
recipient to perform more expensive actions (and it returns a failure code
|
||||
instead of automatically propagating the error). This might include calling back
|
||||
|
||||
@@ -7,9 +7,8 @@ Mapping Types
|
||||
Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables
|
||||
of mapping type are declared using the syntax ``mapping(_KeyType => _ValueType) _VariableName``.
|
||||
The ``_KeyType`` can be any
|
||||
built-in value type plus ``bytes`` and ``string``. User-defined
|
||||
or complex types such as contract types, enums, mappings, structs or array types
|
||||
apart from ``bytes`` and ``string`` are not allowed.
|
||||
built-in value type, ``bytes``, ``string``, or any contract or enum type. Other user-defined
|
||||
or complex types, such as mappings, structs or array types are not allowed.
|
||||
``_ValueType`` can be any type, including mappings, arrays and structs.
|
||||
|
||||
You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialised
|
||||
|
||||
@@ -276,17 +276,17 @@ Example::
|
||||
arbitrary arguments and would also handle a first argument of type
|
||||
``bytes4`` differently. These edge cases were removed in version 0.5.0.
|
||||
|
||||
It is possible to adjust the supplied gas with the ``.gas()`` modifier::
|
||||
It is possible to adjust the supplied gas with the ``gas`` modifier::
|
||||
|
||||
address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
|
||||
Similarly, the supplied Ether value can be controlled too::
|
||||
|
||||
address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
|
||||
Lastly, these modifiers can be combined. Their order does not matter::
|
||||
|
||||
address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
|
||||
In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used.
|
||||
|
||||
@@ -297,7 +297,8 @@ Since byzantium ``staticcall`` can be used as well. This is basically the same a
|
||||
|
||||
All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
|
||||
|
||||
The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``.
|
||||
The ``gas`` option is available on all three methods, while the ``value`` option is not
|
||||
supported for ``delegatecall``.
|
||||
|
||||
.. note::
|
||||
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
||||
@@ -635,8 +636,12 @@ External (or public) functions have the following members:
|
||||
|
||||
* ``.address`` returns the address of the contract of the function.
|
||||
* ``.selector`` returns the :ref:`ABI function selector <abi_function_selector>`
|
||||
* ``.gas(uint)`` returns a callable function object which, when called, will send the specified amount of gas to the target function. See :ref:`External Function Calls <external-function-calls>` for more information.
|
||||
* ``.value(uint)`` returns a callable function object which, when called, will send the specified amount of wei to the target function. See :ref:`External Function Calls <external-function-calls>` for more information.
|
||||
* ``.gas(uint)`` returns a callable function object which, when called, will send
|
||||
the specified amount of gas to the target function. Deprecated - use ``{gas: ...}`` instead.
|
||||
See :ref:`External Function Calls <external-function-calls>` for more information.
|
||||
* ``.value(uint)`` returns a callable function object which, when called, will
|
||||
send the specified amount of wei to the target function. Deprecated - use ``{value: ...}`` instead.
|
||||
See :ref:`External Function Calls <external-function-calls>` for more information.
|
||||
|
||||
Example that shows how to use the members::
|
||||
|
||||
@@ -651,6 +656,8 @@ Example that shows how to use the members::
|
||||
|
||||
function g() public {
|
||||
this.f.gas(10).value(800)();
|
||||
// New syntax:
|
||||
// this.f{gas: 10, value: 800}()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+238
-1
@@ -244,7 +244,7 @@ Input Description
|
||||
// "default", "strip", "debug" and "verboseDebug".
|
||||
// "default" does not inject compiler-generated revert strings and keeps user-supplied ones.
|
||||
// "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects
|
||||
// "debug" injects strings for compiler-generated internal reverts (not yet implemented)
|
||||
// "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now.
|
||||
// "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented)
|
||||
"revertStrings": "default"
|
||||
}
|
||||
@@ -474,3 +474,240 @@ Error types
|
||||
11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue.
|
||||
12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue.
|
||||
13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible.
|
||||
|
||||
|
||||
.. _compiler-tools:
|
||||
|
||||
Compiler tools
|
||||
**************
|
||||
|
||||
solidity-upgrade
|
||||
----------------
|
||||
|
||||
``solidity-upgrade`` can help you to semi-automatically upgrade your contracts
|
||||
to breaking language changes. While it does not and cannot implement all
|
||||
required changes for every breaking release, it still supports the ones, that
|
||||
would need plenty of repetitive manual adjustments otherwise.
|
||||
|
||||
.. note::
|
||||
|
||||
``solidity-upgrade`` carries out a large part of the work, but your
|
||||
contracts will most likely need further manual adjustments. We recommend
|
||||
using a version control system for your files. This helps reviewing and
|
||||
eventually rolling back the changes made.
|
||||
|
||||
.. warning::
|
||||
|
||||
``solidity-upgrade`` is not considered to be complete or free from bugs, so
|
||||
please use with care.
|
||||
|
||||
How it works
|
||||
~~~~~~~~~~~~
|
||||
|
||||
You can pass (a) Solidity source file(s) to ``solidity-upgrade [files]``. If
|
||||
these make use of ``import`` statement which refer to files outside the
|
||||
current source file's directory, you need to specify directories that
|
||||
are allowed to read and import files from, by passing
|
||||
``--allow-paths [directory]``. You can ignore missing files by passing
|
||||
``--ignore-missing``.
|
||||
|
||||
``solidity-upgrade`` is based on ``libsolidity`` and can parse, compile and
|
||||
analyse your source files, and might find applicable source upgrades in them.
|
||||
|
||||
Source upgrades are considered to be small textual changes to your source code.
|
||||
They are applied to an in-memory representation of the source files
|
||||
given. The corresponding source file is updated by default, but you can pass
|
||||
``--dry-run`` to simulate to whole upgrade process without writing to any file.
|
||||
|
||||
The upgrade process itself has two phases. In the first phase source files are
|
||||
parsed, and since it is not possible to upgrade source code on that level,
|
||||
errors are collected and can be logged by passing ``--verbose``. No source
|
||||
upgrades available at this point.
|
||||
|
||||
In the second phase, all sources are compiled and all activated upgrade analysis
|
||||
modules are run alongside compilation. By default, all available modules are
|
||||
activated. Please read the documentation on
|
||||
:ref:`available modules <upgrade-modules>` for further details.
|
||||
|
||||
|
||||
This can result in compilation errors that may
|
||||
be fixed by source upgrades. If no errors occur, no source upgrades are being
|
||||
reported and you're done.
|
||||
If errors occur and some upgrade module reported a source upgrade, the first
|
||||
reported one gets applied and compilation is triggered again for all given
|
||||
source files. The previous step is repeated as long as source upgrades are
|
||||
reported. If errors still occur, you can log them by passing ``--verbose``.
|
||||
If no errors occur, your contracts are up to date and can be compiled with
|
||||
the latest version of the compiler.
|
||||
|
||||
.. _upgrade-modules:
|
||||
|
||||
Available upgrade modules
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+-----------------+---------+--------------------------------------------------+
|
||||
| Module | Version | Description |
|
||||
+=================+=========+==================================================+
|
||||
| ``constructor`` | 0.5.0 | Constructors must now be defined using the |
|
||||
| | | ``constructor`` keyword. |
|
||||
+-----------------+---------+--------------------------------------------------+
|
||||
| ``visibility`` | 0.5.0 | Explicit function visibility is now mandatory, |
|
||||
| | | defaults to ``public``. |
|
||||
+-----------------+---------+--------------------------------------------------+
|
||||
| ``abstract`` | 0.6.0 | The keyword ``abstract`` has to be used if a |
|
||||
| | | contract does not implement all its functions. |
|
||||
+-----------------+---------+--------------------------------------------------+
|
||||
| ``virtual`` | 0.6.0 | Functions without implementation outside an |
|
||||
| | | interface have to be marked ``virtual``. |
|
||||
+-----------------+---------+--------------------------------------------------+
|
||||
| ``override`` | 0.6.0 | When overriding a function or modifier, the new |
|
||||
| | | keyword ``override`` must be used. |
|
||||
+-----------------+---------+--------------------------------------------------+
|
||||
|
||||
Please read :doc:`0.5.0 release notes <050-breaking-changes>` and
|
||||
:doc:`0.6.0 release notes <060-breaking-changes>` for further details.
|
||||
|
||||
Synopsis
|
||||
~~~~~~~~
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Usage: solidity-upgrade [options] contract.sol
|
||||
|
||||
Allowed options:
|
||||
--help Show help message and exit.
|
||||
--version Show version and exit.
|
||||
--allow-paths path(s)
|
||||
Allow a given path for imports. A list of paths can be
|
||||
supplied by separating them with a comma.
|
||||
--ignore-missing Ignore missing files.
|
||||
--modules module(s) Only activate a specific upgrade module. A list of
|
||||
modules can be supplied by separating them with a comma.
|
||||
--dry-run Apply changes in-memory only and don't write to input
|
||||
file.
|
||||
--verbose Print logs, errors and changes. Shortens output of
|
||||
upgrade patches.
|
||||
--unsafe Accept *unsafe* changes.
|
||||
|
||||
|
||||
|
||||
Bug Reports / Feature requests
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you found a bug or if you have a feature request, please
|
||||
`file an issue <https://github.com/ethereum/solidity/issues/new/choose>`_ on Github.
|
||||
|
||||
|
||||
Example
|
||||
~~~~~~~
|
||||
|
||||
Assume you have the following contracts you want to update declared in ``Source.sol``:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
// This will not compile
|
||||
pragma solidity >0.4.23;
|
||||
|
||||
contract Updateable {
|
||||
function run() public view returns (bool);
|
||||
function update() public;
|
||||
}
|
||||
|
||||
contract Upgradable {
|
||||
function run() public view returns (bool);
|
||||
function upgrade();
|
||||
}
|
||||
|
||||
contract Source is Updateable, Upgradable {
|
||||
function Source() public {}
|
||||
|
||||
function run()
|
||||
public
|
||||
view
|
||||
returns (bool) {}
|
||||
|
||||
function update() {}
|
||||
function upgrade() {}
|
||||
}
|
||||
|
||||
|
||||
Required changes
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
To bring the contracts up to date with the current Solidity version, the
|
||||
following upgrade modules have to be executed: ``constructor``,
|
||||
``visibility``, ``abstract``, ``override`` and ``virtual``. Please read the
|
||||
documentation on :ref:`available modules <upgrade-modules>` for further details.
|
||||
|
||||
Running the upgrade
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In this example, all modules needed to upgrade the contracts above,
|
||||
are available and all of them are activated by default. Therefore you
|
||||
do not need to specify the ``--modules`` option.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
$ solidity-upgrade Source.sol --dry-run
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Running analysis (and upgrade) on given source files.
|
||||
..............
|
||||
|
||||
After upgrade:
|
||||
|
||||
Found 0 errors.
|
||||
Found 0 upgrades.
|
||||
|
||||
The above performs a dry-ran upgrade on the given file and logs statistics after all.
|
||||
In this case, the upgrade was successful and no further adjustments are needed.
|
||||
|
||||
Finally, you can run the upgrade and also write to the source file.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
$ solidity-upgrade Source.sol
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Running analysis (and upgrade) on given source files.
|
||||
..............
|
||||
|
||||
After upgrade:
|
||||
|
||||
Found 0 errors.
|
||||
Found 0 upgrades.
|
||||
|
||||
|
||||
Review changes
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The command above applies all changes as shown below. Please review them carefully.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
pragma solidity >0.4.23;
|
||||
|
||||
abstract contract Updateable {
|
||||
function run() public view virtual returns (bool);
|
||||
function update() public virtual;
|
||||
}
|
||||
|
||||
abstract contract Upgradable {
|
||||
function run() public view virtual returns (bool);
|
||||
function upgrade() public virtual;
|
||||
}
|
||||
|
||||
contract Source is Updateable, Upgradable {
|
||||
constructor() public {}
|
||||
|
||||
function run()
|
||||
public
|
||||
view
|
||||
override(Updateable,Upgradable)
|
||||
returns (bool) {}
|
||||
|
||||
function update() public override {}
|
||||
function upgrade() public override {}
|
||||
}
|
||||
|
||||
+655
-316
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user