坚固性v0.8.0突破性变化

本节重点介绍Solidity版本0.8.0中引入的主要突破性更改。有关完整列表,请查看 the release changelog

无声的语义变化

本节列出现有代码在没有编译器通知的情况下更改其行为的更改。

  • 下溢和上溢时恢复算术运算。您可以使用 unchecked {{ ... }} 若要使用先前的包装行为,请执行以下操作。

    检查溢出是非常常见的,所以我们将其设为默认值以提高代码的可读性,即使这会略微增加汽油成本。

  • 默认情况下,ABI编码器v2处于激活状态。

    您可以使用以下命令选择使用旧行为 pragma abicoder v1; 。语料库 pragma experimental ABIEncoderV2; 仍然有效,但已弃用并且没有任何效果。如果您想明确表示,请使用 pragma abicoder v2; 取而代之的是。

    请注意,ABI编码器v2比v1支持更多的类型,并对输入执行更多的健全性检查。当ABI编码器v2包含不符合参数类型的数据时,ABI编码器v2会使一些函数调用变得更昂贵,并且它还可以使未使用ABI编码器v1还原的协定调用还原。

  • 幂运算是右结合的,即表达式 a**b**c 被解析为 a**(b**c) 。在0.8.0之前,它被解析为 (a**b)**c

    这是解析求幂运算符的常用方法。

  • 失败的断言和其他内部检查(如被零除或算术溢出)不会使用无效操作码,而是使用REVERT操作码。更具体地说,它们将使用等于函数调用的错误数据 Panic(uint256) 具有特定于环境的错误代码。

    这将节省错误方面的成本,同时仍然允许静电分析工具将这些情况与无效输入(如失败)的恢复区分开来 require

  • 如果访问存储中长度编码不正确的字节数组,则会导致死机。除非使用内联程序集修改存储字节数组的原始表示形式,否则协定无法进入这种情况。

  • 如果在数组长度表达式中使用常量,则以前版本的实心度将在求值树的所有分支中使用任意精度。现在,如果将常量变量用作中间表达式,则它们的值将以与在运行时表达式中使用时相同的方式正确舍入。

  • 类型 byte 已被移除。那是一个别名 bytes1

新的限制

本节列出可能导致现有合同不再编译的更改。

  • 对文字的显式转换有新的限制。在以下情况下,以前的行为可能是模棱两可的:

    1. 从负文字和大于以下的文字进行显式转换 type(uint160).maxaddress 是不允许的。

    2. 文字和整数类型之间的显式转换 T 仅当文本位于 type(T).mintype(T).max 。具体地说,要替换的用法是 uint(-1) 使用 type(uint).max

    3. 只有当文字可以表示枚举中的值时,才允许在文字和枚举之间进行显式转换。

    4. 文字和文本之间的显式转换 address 类型(例如 address(literal) )有这样的类型 address 而不是 address payable 。人们可以通过使用显式转换来获得付费地址类型,即, payable(literal)

  • Address literals 有这样的类型 address 而不是 address payable 。它们可以转换为 address payable 通过使用显式转换,例如 payable(0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF)

  • 对显式类型转换有新的限制。仅当符号、宽度或类型类别最多更改一次时才允许转换 (intaddressbytesNN 等)。要执行多个更改,请使用多个转换。

    让我们使用记号 T(S) 表示显式转换 T(x) ,在哪里, TS 是类型,并且 x 是类型为的任意变量 S 。这种不允许的转换的一个例子是 uint16(int8) 因为它将宽度(8位更改为16位)和符号(有符号整数更改为无符号整数)。为了进行转换,必须经过中间类型。在上一个示例中,这将是 uint16(uint8(int8))uint16(int16(int8)) 。请注意,这两种转换方式将产生不同的结果,例如,对于 -1 。以下是此规则不允许的一些转换示例。

    • address(uint)uint(address) :同时转换文字类别和宽度。将此替换为 address(uint160(uint))uint(uint160(address)) 分别为。

    • payable(uint160)payable(bytes20)payable(integer-literal) :同时转换类型类别和状态可变。将此替换为 payable(address(uint160))payable(address(bytes20))payable(address(integer-literal)) 分别为。请注意, payable(0) 是有效的,并且是规则的例外。

    • int80(bytes10)bytes10(int80) :同时转换TYPE-CATEGORY和SIGN。将此替换为 int80(uint80(bytes10))bytes10(uint80(int80) 分别为。

    • Contract(uint) :同时转换文字类别和宽度。将此替换为 Contract(address(uint160(uint)))

    这些转换是不允许的,以避免模棱两可。例如,在表达式中 uint16 x = uint16(int8(-1)) ,的价值 x 将取决于首先应用符号还是宽度转换。

  • 函数调用选项只能提供一次,即 c.f{{gas: 10000}}{{value: 1}}() 无效,必须更改为 c.f{{gas: 10000, value: 1}}()

  • 全局函数 log0log1log2log3log4 已经被移走了。

    这些是在很大程度上未使用的低级函数。它们的行为可以从内联程序集访问。

  • enum 定义包含的成员不能超过256个。

    这将使您可以安全地假设ABI中的基础类型始终是 uint8

  • 名称为 thissuper_ 是不允许的,公共函数和事件除外。例外情况是,可以声明使用不允许使用此类函数名称的语言实现的约定的接口。

  • 删除对 \b\f ,以及 \v 代码中的转义序列。它们仍然可以通过十六进制转义插入,例如 \x08\x0c ,以及 \x0b ,分别为。

  • 全局变量 tx.originmsg.sender 有这样的类型 address 而不是 address payable 。你可以把它们转换成 address payable 通过使用显式转换,即, payable(tx.origin)payable(msg.sender)

    之所以进行此更改,是因为编译器无法确定这些地址是否可支付,因此现在需要显式转换才能使此要求可见。

  • 显式转换为 address 类型始终返回不可支付的 address 键入。特别是,以下显式转换的类型 address 而不是 address payable

    • address(u) 哪里 u 是类型为 uint160 。一个人可以转换成 u 输入到文字中 address payable 通过使用两个显式转换,即, payable(address(u))

    • address(b) 哪里 b 是类型为 bytes20 。一个人可以转换成 b 输入到文字中 address payable 通过使用两个显式转换,即, payable(address(b))

    • address(c) 哪里 c 是一份合同。以前,此转换的返回类型取决于合约是否可以接收以太(通过具有接收功能或支付回退功能)。转换 payable(c) 具有这样的类型 address payable 并且只有当合同 c 可以接受以太。一般说来,人们总是可以转换成 c 输入到文字中 address payable 通过使用以下显式转换: payable(address(c)) 。请注意, address(this) 属于与…相同的类别 address(c) 同样的规则也适用于它。

  • 这个 chainid 现在考虑内联程序集中的内置 view 而不是 pure

  • 一元求反不能再用于无符号整数,只能用于有符号整数。

界面更改

  • 的输出 --combined-json 已更改:JSON字段 abidevdocuserdocstorage-layout 现在是子对象。在0.8.0之前,它们通常被序列化为字符串。

  • “遗留AST”已被移除 (--ast-json 在命令行界面上, legacyAST 用于标准JSON)。使用“紧凑AST” (--ast-compact--json 响应。 AST )作为替代。

  • 老错误记者 (--old-reporter )已删除。

如何更新您的代码

  • 如果您依赖于换行算法,请将每个运算用 unchecked {{ ... }}

  • 可选:如果使用SafeMath或类似的库,请更改 x.add(y)x + yx.mul(y)x * y 等。

  • 添加 pragma abicoder v1; 如果您想继续使用旧的ABI编码器。

  • 可以选择删除 pragma experimental ABIEncoderV2pragma abicoder v2 因为它是多余的。

  • 改变 bytebytes1

  • 如果需要,添加中间显式类型转换。

  • 联合收割机 c.f{{gas: 10000}}{{value: 1}}()c.f{{gas: 10000, value: 1}}()

  • 改变 msg.sender.transfer(x)payable(msg.sender).transfer(x) 或使用存储的变量 address payable 键入。

  • 改变 x**y**z(x**y)**z

  • 使用内联程序集替代 log0 ,., log4

  • 通过从类型的最大值中减去无符号整数并加1(例如 type(uint256).max - x + 1 ,同时确保 x 不是零)