我们知道,合约结构包含 状态变量、函数、函数修改器(modifier)、事件(event)、结构体(struct)和枚举类型(enum)。
在^0.8.4版本,合约结构增加了错误(error),为应对失败时,错误可以在revert 中使用。与错误字符串相比,error花费更少的gas(即更便宜),并且允许编码额外的数据,还可以使用natspec注释形式。
举个栗子,代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @dev 没有足够的金额
/// @param _balance表示当前合约账户余额
/// @param _amount 转账金额
error NotEnoughFunds(uint _balance,uint _amount);
contract TestError {
/// @dev 事件 - 记录转账信息
event Tansfer(address _from, address _to, uint _amount);
/// @dev 当合约账户余额小于转账金额时,使用字符串 revert
function transfer(address _to, uint _amount) external {
if (address(this).balance < _amount) {
revert("no enough funds");
}
emit Tansfer(msg.sender, _to, _amount);
}
/// @dev 当合约账户余额小于转账金额时,使用error revert
function transferWithError(address _to, uint _amount) external {
if (address(this).balance < _amount) {
revert NotEnoughFunds(address(this).balance, _amount);
}
emit Tansfer(msg.sender, _to, _amount);
}
}
error同struct一样,可以写在合约里,也可以写在合约外面,只要作用域不同,同一个error可以在多个地方定义,以上代码写在了合约外面(可以被其它合约使用),同时使用了natspec注释形式。
在应用二进制接囗(ABI)的JSON描述中,同事件、函数、合约中方法一样,同样包含了error,如下所示:
{
"inputs": [
{
"internalType": "uint256",
"name": "_balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "NotEnoughFunds",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "_from",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "Tansfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "transferWithError",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
error可以继承,但不能被重写。重写条件为父合约标记为virtual函数可以在继承合约里重写,error不能标记为virtual。
测试
使用Remix中JavaScript VM部署合约,执行合约中两个函数(一个使用error结构,另一个使用错误字符串),验证失败时花费的gas:
(1)执行transfer函数(使用错误字符串)
执行后可以看到错误信息提示“no enough funds”,花费的gas为22513
(2)执行transferWithError函数(使用error结构)
执行后可以看到错误中输出的参数值,感觉与前端的console.log一样,可以当作调试信息,花费的gas为22497,比上面少了16个gas
若部署到测试网,如 Popsten,执行函数会直接先提示(如下图所示),若强制进行交易,则与上面结果相同。