WTF Solidity: Manejo de Errores en Solidity — Guía Práctica deerror,requireyassert【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity导读本篇文章基于 WTF-Solidity 教程第 15 讲西语版教程系统讲解 Solidity 中三种抛出异常的语句error、require和assert。通过完整的合约源码、Remix 控制台实测截图与 gas 消耗对比你将掌握如何在智能合约中正确地校验前置条件、向用户解释失败原因并在编写合约时做出最省 gas 的异常处理选择。智能合约一旦部署上链便无法修改任何未预期的状态都可能造成资金损失。Solidity 提供了多套异常处理机制来保证失败即回滚revert的原子性语义但它们在使用方式、可读性与 gas 开销上差异明显。本文以 WTF-Solidity 仓库中Errors合约的实际代码为主线逐一拆解error、require、assert的语法、适用场景与实测 gas 成本。Solidity 中的异常编译期错误与运行期错误Solidity 的异常可以发生在两个阶段编译期compile time如类型不匹配、语法错误在编译阶段就被编译器拦截运行期runtime如条件校验失败、算术溢出、调用不存在的函数等此时交易会整体回滚revert所有状态变更被撤销。本篇聚焦运行期异常。Solidity 0.8之后语言内置了算术溢出检查开发者对异常的掌控主要围绕三种语句展开errorrevertSolidity0.8.4起引入的自定义错误机制require(condition, message)0.8之前最常用的校验语句assert(condition)面向内部逻辑断言不带错误消息。下面结合仓库中的 Error.sol 源码逐一说明。使用error自定义错误省 gas 且信息清晰声明自定义错误error是 Solidity0.8.4版本新增的特性也是当前官方推荐的首选异常方式。它可以在合约内部或外部声明且可以携带参数既省 gas 又能向调用者清晰说明失败原因还方便开发者调试。在 Error.sol 中我们在合约外部声明了一个最简单的自定义错误// SPDX-License-Identifier: MIT pragma solidity ^0.8.34; // Declaración de error TransferNotOwner error TransferNotOwner();也可以声明一个携带参数的错误把谁发起了非法操作一并抛出便于链下索引与调试error TransferNotOwner(address sender); // error personalizado con parámetro用revert触发错误在函数体中error必须配合revert语句使用当条件不满足时执行revert TransferNotOwner();交易即刻回滚并携带错误信息。仓库中的transferOwner1()完整实现了这一模式见 Error.solcontract Errors{ // Un mapping que registra el propietario para cada TokenId mapping(uint256 address) private _owners; // Error : precio de gas 24445 function transferOwner1(uint256 tokenId, address newOwner) public { if(_owners[tokenId] ! msg.sender){ revert TransferNotOwner(); // revert TransferNotOwner(msg.sender); } _owners[tokenId] newOwner; } ... }该函数先校验当前调用者msg.sender是否为该tokenId的 owner若不是则抛出TransferNotOwner并回滚若是则更新 owner。把revert TransferNotOwner(msg.sender);的注释打开即可改为携带调用者地址的带参版本。补充说明error携带参数时 gas 会略增仓库实测约 24660 wei但仍低于require携带字符串的版本且信息表达能力更强。require经典的条件校验语句require是 Solidity0.8之前最主流的异常处理方式至今仍在大量主流合约中使用。其语法为require(condition, mensaje de error);当condition不成立时抛出异常mensaje de error会随异常一起返回给调用方便于用户理解失败原因。用require重写上面的转账函数Error.sol// requirer : precio de gas 24743 function transferOwner2(uint256 tokenId, address newOwner) public { require(_owners[tokenId] msg.sender, Transfer Not Owner); _owners[tokenId] newOwner; }require的优点是简单直观但存在一个显著缺点gas 消耗随错误字符串长度的增长而线性上升。因为字符串需要作为 calldata 存储并返回消息越长成本越高。这也是error相比require的核心优势之一——自定义错误名以 selector 形式编码开销与消息长度无关。assert面向调试的不变量断言assert通常用于程序开发阶段的debug它不携带任何错误消息因此无法向用户解释失败原因一般不建议用在面向用户的接口上。语法如下assert(condition);条件不成立即抛出异常。用assert重写转账函数Error.sol// Afirmar : precio de gas 24446 function transferOwner3(uint256 tokenId, address newOwner) public { assert(_owners[tokenId] msg.sender); _owners[tokenId] newOwner; }值得注意的版本差异在 Solidity0.8.0之前的版本中assert抛出的是panic exception会消耗掉剩余的全部 gas 且不返还因此当年被严格限制用于理论上绝不可能失败的内部不变量检查0.8.0之后assert与其他异常语句一样仅消耗固定开销但语义上仍被保留用于内部一致性断言如assert(balance totalSupply)。Remix 实操验证三种异常的差别一览在 Remix IDE 中部署Errors合约后Remix编译器建议使用仓库注释对应的0.8.17版本以获得与本文一致的 gas 数据依次调用三个函数传入任意uint256数字与非零地址即可在控制台看到三种截然不同的反馈对应 Error.sol 中实现。1. 调用transferOwner1()error方式控制台抛出异常并直接显示自定义错误名TransferNotOwner信息简洁、定位精准。2. 调用transferOwner2()require方式控制台抛出异常并打印require中定义的字符串消息Transfer Not Owner可读性强但成本更高。3. 调用transferOwner3()assert方式控制台仅提示交易回滚没有任何错误消息用户无从得知失败原因。三种方式的输出差异一目了然error输出错误名、require输出字符串、assert仅回滚无提示。Gas 对比究竟哪种方式最省钱通过 Remix 控制台的Debug按钮可以查看每次函数调用的实际 gas 消耗。使用0.8.17版本编译、以tokenId 123与非零地址测试相关数据记录于 15_Errors/Error.sol 与 中文原版教程三种方式对比结果如下异常方式调用函数gas 消耗weierror无参数transferOwner124457error带参数sendertransferOwner124660require带字符串消息transferOwner224755assert无消息transferOwner324473结论非常明确error的 gas 最少其次是assertrequire消耗最多。也就是说error既能向用户说明失败原因又能节省 gas是三种方式中的最优解应在实际开发中优先使用。需要补充两点注意事项数值会因测试时间、编译版本不同而略有浮动例如仓库西语版 Error.sol 注释中的数值为 24445 / 24743 / 24446但三种方式的相对大小关系始终一致不影响结论。require不带消息时反而比assert便宜不带消息的revert返回空数据而assert需要返回固定 36 字节的Panic(uint256)错误数据。但二者都无法告知用户失败原因实际开发中仍推荐使用error。总结本讲基于 WTF-Solidity 的Errors合约Error.sol中文原版见 15_Errors/Error.sol介绍了 Solidity 三种抛出异常的方式errorrevertSolidity0.8.4新增支持自定义错误名与参数gas 最省是官方推荐的首选方式require0.8之前的经典校验语句带字符串消息可读性好但 gas 随消息长度线性增长综合成本最高assert面向内部逻辑断言无错误消息历史上0.8之前会耗尽剩余 gas仅适合调试与不变量检查。实际开发建议面向用户的参数校验与业务逻辑校验优先使用error内部不变量检查再考虑assertrequire可作为兼容旧代码风格的备选。若想完整运行验证可直接在 Remix 中部署仓库中的 Error.sol并参考中文原版教程 15_Errors/readme.md 中的补充说明。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考