Solidity v0.6.0重大更新

本节重点介绍Solidity 版本0.6.0 中引入的重大突破性变化,以及更改背后的原因以及如何更新受影响的代码。

有关完整列表,请检查 更新日志.

更改编译器不用关注的修改

本节列出了代码行为可能发生的更改, 而编译器不会告诉您。

  • 求幂的结果类型是基数的类型。 曾经是最小的可以容纳基数和指数的类型, 此外,幂的基数类型允许使用带符号的类型。

需要明确指定的更改

本节列出了现在需要更明确地显示代码的更改, 但是语义不会改变。 对于大多数情况,编译器将提供建议。

  • 现在,仅当函数被标记为 virtual 或在接口中定义 时,才可以覆盖。

   在接口外部没有实现的函数必须标记为 virtual 。    覆盖函数或修饰符时,必须使用新关键字 override 。 覆盖多个基类定义的函数或修饰符时, 需要像这样 override(Base1, Base2) 列出.

  • 访问数组的 length 现在是只读的, 即便是storage 数组. 不能通过修改length来调整数组的大小, 必须通过函数 push(), push(value)pop() 或者分配一个完整的数组(不过这会覆盖现有内容)。 这个修改背后的原因是为了防止存储冲突.
  • 关键字 abstract 用来标记一个抽象合约. 如果合约有没有实现的函数, 需要使用。抽象合约不能通过 new 操作符创建,并且不能在编译期生成字节码(bytecode)。
  • 库需要实现所有的函数,不仅仅是内部函数.
  • 内联汇编中声明的变量名称可以不再以 _slot_offset 结尾。
  • 内联汇编中的变量声明可能不会在内联汇编块之外屏蔽声明。

  如果名称包含一个点,则该点之前的前缀可能与内联汇编块外部的任何声明都没有冲突。

  • 现在不允许状态变量屏蔽。 派生合约只能声明基类合约中都没有同名的可见状态变量 x (即如果基类合约中有共有的 状态变量 x, 则派生合约不能在声明 x )。

语义和句法变化

本节列出了必须修改代码的更改.

  • 现在禁止从外部函数类型到 address 的转换。 现在外部函数类型具有一个称为 address 的成员属性,类似于现有的 selector 成员。
  • 对于动态的存储数组, 函数 push(value) 不再返回长度(现在什么都不返回).
  • 未命名的函数通常称为 fallback function ,已拆分为一个使用 fallback 关键字定义的回退函数 和 使用关键字 receive 定义的接受以太函数.
    • 如果有``receive’’函数, 向合约转账的时候会调用,不管是否是调用数据(即便没有以太接收), receive 函数隐含带有 payable .
    • 新的回退函数会被调用,当没有其他的函数被匹配时, (如果不存在 receive 函数,则没有调用数据的转账也会调用回退函数). 回退函数可以标记为 payable (也可以不标记). 如果没有 payable 那么附加以太的调用会revert.

    如果是需要实现升级或代理模式,则仅需要实现新的回退函数。

新特性

本节列出了在Solidity 0.6.0 之前不可能实现的事情,或者在Solidity 0.6.0之前很难实现的事情。

  • 加入了 try/catch 语句 使您可以对失败的外部调用做出反应。
  • structenum 类型可以在文件级别声明.
  • 数组切片可以用于 calldata 数组, 例如: abi.decode(msg.data[4:], (uint, uint)) 是一个对函数调用payload进行解码底层方法。
  • Natspec在开发人员文档中支持多个返回参数,并强制执行与 @param 的同名检查。
  • Yul和Inline Assembly有一个名为 leave 的新语句,该语句退出当前函数.
  • 现在可以通过 payable(x)address 转换为 address payable , 当然 x 需要是 地址类型.

接口变化

本节列出了与语言本身无关的更改,但这些更改会影响编译器的接口。 如果命令行上使用编译器的, 或编程方式使用, 或分析编译器输出时, 都会产生改变.

新错误报告器

A new error reporter was introduced, which aims at producing more accessible error messages on the command line. It is enabled by default, but passing --old-reporter falls back to the the deprecated old error reporter.

Metadata 哈希选项

The compiler now appends the IPFS hash of the metadata file to the end of the bytecode by default (for details, see documentation on contract metadata). Before 0.6.0, the compiler appended the Swarm hash by default, and in order to still support this behaviour, the new command line option --metadata-hash was introduced. It allows you to select the hash to be produced and appended, by passing either ipfs or swarm as value to the --metadata-hash command line option. Passing the value none completely removes the hash.

These changes can also be used via the Standard JSON Interface and effect the metadata JSON generated by the compiler.

The recommended way to read the metadata is to read the last two bytes to determine the length of the CBOR encoding and perform a proper decoding on that data block as explained in the metadata section.

Yul 优化器

Together with the legacy bytecode optimizer, the Yul optimizer is now enabled by default when you call the compiler with --optimize. It can be disabled by calling the compiler with --no-optimize-yul. This mostly affects code that uses ABIEncoderV2.

C API Changes

The client code that uses the C API of libsolc is now in control of the memory used by the compiler. To make this change consistent, solidity_free was renamed to solidity_reset, the functions solidity_alloc and solidity_free were added and solidity_compile now returns a string that must be explicitly freed via solidity_free().

怎样修改代码

本节提供有关如何为每个重大变更更新先前代码的详细说明。

  • 如果 f 是外部函数类型, address(f) 修改为 f.address
  • 接收以太的函数, function () external [payable] { ... } 修改为 receive() external payable { ... }, fallback() external [payable] { ... } 或者两个度加上. 只要有可能,最好是仅仅使用 receive  函数.
  • 修改 uint length = array.push(value)array.push(value);. 通过 array.length  获取长度.
  • 修改 array.length++array.push() 去增加storage数组长度,  用 pop() 去减少storage数组长度.
  • 对于函数的 @dev 开发这文档中的每个命名返回参数,请定义一个``@return`` 条目,其中将返回参数名称作为第一个单词。 例如。 已定义函数` f()` 是这样  function f() public returns (uint value) , 记录返回值的文档注释是这样: @return value 这是返回值. 您可以混合使用命名和未命名的返回参数,只要按它们在返回元组类型中出现的顺序排列即可。
  • 为内联汇编中的变量声明选择与内联汇编块外的声明不冲突的唯一标识符。
  • 为每个非接口函数添加 virtual. 对于单继承,请在每个重写函数中添加``override``。 对于多重继承,添加 override(A, B, ..), 在在括号中列出所有覆盖函数的的合约。

  当多个基类合约有相同定义的函数时,继承合约必须覆盖所有冲突的函数。