Solidity 0.6 重大变化
一、新的 fallback 函数
在 0.6 之前的版本,我们可以定义下面的 fallback 函数,用来通过合约接收 eth 转账或未指定明确合约函数的调用。
function() external payable {
}
从 0.6 开始,fallback 函数分成了两个函数,一个新的回调函数使用 fallback 关键字和一个接收 ether 的函数使用 receive 关键字。写法是下面这样的:
fallback() external {
}
receive() payable external {
}
对于这种新的写法,有几点是要注意的:
- fallback 和 receive 不是普通函数,而是新的函数类型,有特别的含义,所以不能在它们前面加 function 这个关键字。加上 function 之后,它们就变成了一般的函数,只能按一般函数来去调用。
- 每个合约最多有一个不带任何参数不带 function 关键字的 fallback 和 receive 函数。
- receive 函数类型必须是 payable 的,并且里面的语句只有在通过外部地址往合约里转账的时候执行。
- fallback 函数类型可以是 payable 也可以不是 payable 的,如果不是 payable 的,可以往合约发送非转账交易,如果交易里带有转账信息,交易会被 revert;如果是 payable 的,自然也就可以接受转账了。
- 尽管 fallback 可以是 payable 的,但并不建议这么做,声明为 payable 之后,其所消耗的 gas 最大量就会被限定在 2300。
二、对合约继承更好的支持
这个版本之前合约继承可以这么写的,看起来比较简单,语义上并不是很清晰。
contract Employee {
function getSalary() public;
}
contract Manager is Employee {
function increaseSalary() public {
}
function getSalary() public {
}
}
从 0.6 开始,solidity 引入了 abstract, virtual, override 几个关键字,继承关系需要用下面的写法。
abstract contract Employee {
function getSalary() public virtual;
}
contract Manager is Employee {
function increaseSalary() public {
}
function getSalary() public override {
}
}
三、对 try...catch 机制的支持
在之前的 solidity 版本,我们在当前合约发起对外部合约调用的话,如果外部合约调用执行失败被 revert,外部合约状态会被回滚,当前合约状态也会被回滚。但有时候我们并不想这样,要是能够捕获外部合约调用异常,然后根据情况做自己的处理不是更好么?
// 这是我们需要调用的外部合约接口
interface DataFeed { function getData(address token) external returns (uint value); }
contract FeedConsumer {
DataFeed feed;
uint errorCount;
function rate(address token) public returns (uint value, bool success) {
// 如果外部合约调用错误次数超过10次,就不再进行更多调用了
require(errorCount < 10);
try feed.getData(token) returns (uint v) {
return (v, true);
} catch Error(string memory /* 出错原因 */) {
// 这块儿代码只有在对DataFeed调用失败时才会执行,
// 通常是不满足外部合约require语句条件或触发revert语句时所引起的调用失败
errorCount++;
return (0, false);
} catch (bytes memory) {
// 当外部调用触发assert语句或除0等比较严重错误时会执行这个catch块
errorCount++;
return (0, false);
}
}
}
四、其它
上面只是列了几个比较大的变化,还有一些其它变化也是值得注意的:
- 动态数组的长度从 0.6 开始不可更改了。
- 开始部分支持数组切片了。
- 结构体和枚举类型可以在合约外声明了,之前是只能在合约内声明的。
- 如果父合约声明了某个非 private 的状态变量,子合约中就不能再声明同名状态变量。
- 从 address 到 address payable 的转换现在可以通过 payable(x)进行,其中 x 必须是 address 类型。