Solidity v0.8.0 重大更新¶
This section highlights the main breaking changes introduced in Solidity version 0.8.0. For the full list check the release changelog.
Silent Changes of the Semantics¶
This section lists changes where existing code changes its behaviour without the compiler notifying you about it.
Arithmetic operations revert on underflow and overflow. You can use
unchecked { ... }
to use the previous wrapping behaviour.Checks for overflow are very common, so we made them the default to increase readability of code, even if it comes at a slight increase of gas costs.
ABI coder v2 is activated by default.
You can choose to use the old behaviour using
pragma abicoder v1;
. The pragmapragma experimental ABIEncoderV2;
is still valid, but it is deprecated and has no effect. If you want to be explicit, please usepragma abicoder v2;
instead.Note that ABI coder v2 supports more types than v1 and performs more sanity checks on the inputs. ABI coder v2 makes some function calls more expensive and it can also make contract calls revert that did not revert with ABI coder v1 when they contain data that does not conform to the parameter types.
Exponentiation is right associative, i.e., the expression
a**b**c
is parsed asa**(b**c)
. Before 0.8.0, it was parsed as(a**b)**c
.This is the common way to parse the exponentiation operator.
Failing assertions and other internal checks like division by zero or arithmetic overflow do not use the invalid opcode but instead the revert opcode. More specifically, they will use error data equal to a function call to
Panic(uint256)
with an error code specific to the circumstances.This will save gas on errors while it still allows static analysis tools to distinguish these situations from a revert on invalid input, like a failing
require
.If a byte array in storage is accessed whose length is encoded incorrectly, a panic is caused. A contract cannot get into this situation unless inline assembly is used to modify the raw representation of storage byte arrays.
If constants are used in array length expressions, previous versions of Solidity would use arbitrary precision in all branches of the evaluation tree. Now, if constant variables are used as intermediate expressions, their values will be properly rounded in the same way as when they are used in run-time expressions.
The type
byte
has been removed. It was an alias ofbytes1
.
New Restrictions¶
This section lists changes that might cause existing contracts to not compile anymore.
There are new restrictions related to explicit conversions of literals. The previous behaviour in the following cases was likely ambiguous:
Explicit conversions from negative literals and literals larger than
type(uint160).max
toaddress
are disallowed.Explicit conversions between literals and an integer type
T
are only allowed if the literal lies betweentype(T).min
andtype(T).max
. In particular, replace usages ofuint(-1)
withtype(uint).max
.Explicit conversions between literals and enums are only allowed if the literal can represent a value in the enum.
Explicit conversions between literals and
address
type (e.g.address(literal)
) have the typeaddress
instead ofaddress payable
. One can get a payable address type by using an explicit conversion, i.e.,payable(literal)
.
Address literals have the type
address
instead ofaddress payable
. They can be converted toaddress payable
by using an explicit conversion, e.g.payable(0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF)
.There are new restrictions on explicit type conversions. The conversion is only allowed when there is at most one change in sign, width or type-category (
int
,address
,bytesNN
, etc.). To perform multiple changes, use multiple conversions.Let us use the notation
T(S)
to denote the explicit conversionT(x)
, where,T
andS
are types, andx
is any arbitrary variable of typeS
. An example of such a disallowed conversion would beuint16(int8)
since it changes both width (8 bits to 16 bits) and sign (signed integer to unsigned integer). In order to do the conversion, one has to go through an intermediate type. In the previous example, this would beuint16(uint8(int8))
oruint16(int16(int8))
. Note that the two ways to convert will produce different results e.g., for-1
. The following are some examples of conversions that are disallowed by this rule.address(uint)
anduint(address)
: converting both type-category and width. Replace this byaddress(uint160(uint))
anduint(uint160(address))
respectively.payable(uint160)
,payable(bytes20)
andpayable(integer-literal)
: converting both type-category and state-mutability. Replace this bypayable(address(uint160))
,payable(address(bytes20))
andpayable(address(integer-literal))
respectively. Note thatpayable(0)
is valid and is an exception to the rule.int80(bytes10)
andbytes10(int80)
: converting both type-category and sign. Replace this byint80(uint80(bytes10))
andbytes10(uint80(int80)
respectively.Contract(uint)
: converting both type-category and width. Replace this byContract(address(uint160(uint)))
.
These conversions were disallowed to avoid ambiguity. For example, in the expression
uint16 x = uint16(int8(-1))
, the value ofx
would depend on whether the sign or the width conversion was applied first.Function call options can only be given once, i.e.
c.f{gas: 10000}{value: 1}()
is invalid and has to be changed toc.f{gas: 10000, value: 1}()
.The global functions
log0
,log1
,log2
,log3
andlog4
have been removed.These are low-level functions that were largely unused. Their behaviour can be accessed from inline assembly.
enum
definitions cannot contain more than 256 members.This will make it safe to assume that the underlying type in the ABI is always
uint8
.Declarations with the name
this
,super
and_
are disallowed, with the exception of public functions and events. The exception is to make it possible to declare interfaces of contracts implemented in languages other than Solidity that do permit such function names.Remove support for the
\b
,\f
, and\v
escape sequences in code. They can still be inserted via hexadecimal escapes, e.g.\x08
,\x0c
, and\x0b
, respectively.The global variables
tx.origin
andmsg.sender
have the typeaddress
instead ofaddress payable
. One can convert them intoaddress payable
by using an explicit conversion, i.e.,payable(tx.origin)
orpayable(msg.sender)
.This change was done since the compiler cannot determine whether or not these addresses are payable or not, so it now requires an explicit conversion to make this requirement visible.
Explicit conversion into
address
type always returns a non-payableaddress
type. In particular, the following explicit conversions have the typeaddress
instead ofaddress payable
:address(u)
whereu
is a variable of typeuint160
. One can convertu
into the typeaddress payable
by using two explicit conversions, i.e.,payable(address(u))
.address(b)
whereb
is a variable of typebytes20
. One can convertb
into the typeaddress payable
by using two explicit conversions, i.e.,payable(address(b))
.address(c)
wherec
is a contract. Previously, the return type of this conversion depended on whether the contract can receive Ether (either by having a receive function or a payable fallback function). The conversionpayable(c)
has the typeaddress payable
and is only allowed when the contractc
can receive Ether. In general, one can always convertc
into the typeaddress payable
by using the following explicit conversion:payable(address(c))
. Note thataddress(this)
falls under the same category asaddress(c)
and the same rules apply for it.
The
chainid
builtin in inline assembly is now consideredview
instead ofpure
.Unary negation cannot be used on unsigned integers anymore, only on signed integers.
Interface Changes¶
The output of
--combined-json
has changed: JSON fieldsabi
,devdoc
,userdoc
andstorage-layout
are sub-objects now. Before 0.8.0 they used to be serialised as strings.The “legacy AST” has been removed (
--ast-json
on the commandline interface andlegacyAST
for standard JSON). Use the “compact AST” (--ast-compact--json
resp.AST
) as replacement.The old error reporter (
--old-reporter
) has been removed.
How to update your code¶
If you rely on wrapping arithmetic, surround each operation with
unchecked { ... }
.Optional: If you use SafeMath or a similar library, change
x.add(y)
tox + y
,x.mul(y)
tox * y
etc.Add
pragma abicoder v1;
if you want to stay with the old ABI coder.Optionally remove
pragma experimental ABIEncoderV2
orpragma abicoder v2
since it is redundant.Change
byte
tobytes1
.Add intermediate explicit type conversions if required.
Combine
c.f{gas: 10000}{value: 1}()
toc.f{gas: 10000, value: 1}()
.Change
msg.sender.transfer(x)
topayable(msg.sender).transfer(x)
or use a stored variable ofaddress payable
type.Change
x**y**z
to(x**y)**z
.Use inline assembly as a replacement for
log0
, …,log4
.Negate unsigned integers by subtracting them from the maximum value of the type and adding 1 (e.g.
type(uint256).max - x + 1
, while ensuring that x is not zero)