异常
写智能合约经常会出bug
,Solidity
中的异常命令帮助我们debug
。
Error
error
是solidity 0.8.4版本
新加的内容,方便且高效(省gas
)地向用户解释操作失败的原因,同时还可以在抛出异常的同时携带参数,帮助开发者更好地调试。人们可以在contract
之外定义异常。下面,我们定义一个TransferNotOwner
异常,当用户不是代币owner
的时候尝试转账,会抛出错误:
error TransferNotOwner(); // 自定义error
我们也可以定义一个携带参数的异常,来提示尝试转账的账户地址
error TransferNotOwner(address sender); // 自定义的带参数的error
在执行当中,error
必须搭配revert
(回退)命令使用。
function transferOwner1(uint256 tokenId, address newOwner) public {
if(_owners[tokenId] != msg.sender){
revert TransferNotOwner();
// revert TransferNotOwner(msg.sender);
}
_owners[tokenId] = newOwner;
}
我们定义了一个transferOwner1()
函数,它会检查代币的owner
是不是发起人,如果不是,就会抛出TransferNotOwner
异常;如果是的话,就会转账。
Require
require
命令是solidity 0.8版本
之前抛出异常的常用方法,目前很多主流合约仍然还在使用它。它很好用,唯一的缺点就是gas
随着描述异常的字符串长度增加,比error
命令要高。使用方法:require(检查条件,"异常的描述")
,当检查条件不成立的时候,就会抛出异常。
我们用require
命令重写一下上面的transferOwner1
函数:
function transferOwner2(uint256 tokenId, address newOwner) public {
require(_owners[tokenId] == msg.sender, "Transfer Not Owner");
_owners[tokenId] = newOwner;
}
Assert
assert
命令一般用于程序员写程序debug
,因为它不能解释抛出异常的原因(比require
少个字符串)。它的用法很简单,assert(检查条件)
,当检查条件不成立的时候,就会抛出异常。
我们用assert
命令重写一下上面的transferOwner1
函数:
function transferOwner3(uint256 tokenId, address newOwner) public {
assert(_owners[tokenId] == msg.sender);
_owners[tokenId] = newOwner;
}
函数的重载
重载
Solidity
中允许函数进行重载(overloading
),即名字相同但输入参数类型不同的函数可以同时存在,他们被视为不同的函数。注意,Solidity
不允许修饰器(modifier
)重载。
函数重载
举个例子,我们可以定义两个都叫saySomething()
的函数,一个没有任何参数,输出"Nothing"
;另一个接收一个string
参数,输出这个string
。
function saySomething() public pure returns(string memory){
return("Nothing");
}
function saySomething(string memory something) public pure returns(string memory){
return(something);
}
最终重载函数在经过编译器编译后,由于不同的参数类型,都变成了不同的函数选择(selector)。
实参匹配(Argument Matching)
在调用重载函数时,会把输入的实际参数和函数参数的变量类型做匹配。 如果出现多个匹配的重载函数,则会报错。下面这个例子有两个叫
f()
的函数,一个参数为uint8
,另一个为uint256
:
function f(uint8 _in) public pure returns (uint8 out) {
out = _in;
}
function f(uint256 _in) public pure returns (uint256 out) {
out = _in;
}
我们调用f(50)
,因为50
既可以被转换为uint8
,也可以被转换为uint256
,因此会报错。
演示代码1
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
// Gas cost在Remix中测试得到 使用0.8.17版本编译
// 参数使用 tokenId = 123, address = {any address}
// 自定义error
error TransferNotOwner();
// error TransferNotOwner(address sender);
contract Errors {
// 一组映射,记录每个TokenId的Owner
mapping(uint256 => address) private _owners;
// Error方法: gas cost 24457
// Error with parameter: gas cost 24660
function transferOwner1(uint256 tokenId, address newOwner) public {
if (_owners[tokenId] != msg.sender) {
revert TransferNotOwner();
// revert TransferNotOwner(msg.sender);
}
_owners[tokenId] = newOwner;
}
// require方法: gas cost 24755
function transferOwner2(uint256 tokenId, address newOwner) public {
require(_owners[tokenId] == msg.sender, "Transfer Not Owner");
_owners[tokenId] = newOwner;
}
// assert方法: gas cost 24473
function transferOwner3(uint256 tokenId, address newOwner) public {
assert(_owners[tokenId] == msg.sender);
_owners[tokenId] = newOwner;
}
}
演示代码2
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract Overload {
function saySomething() public pure returns(string memory){
return("Nothing");
}
function saySomething(string memory something) public pure returns(string memory){
return(something);
}
function f(uint8 _in) public pure returns (uint8 out) {
out = _in;
}
function f(uint256 _in) public pure returns (uint256 out) {
out = _in;
}
}