Solidity中解码自定义错误时调用回退的问题排查
自定义错误try-catch解码失败问题
我用Solidity 0.8.4在Remix里写了两个合约,一个通过try-catch调用另一个抛出自定义错误的合约。尝试用abi.decode解码错误原因的bytes时,调用会回退;注释掉解码代码就正常,只解码selector也能工作,但拿不到错误参数。日志里的reason是0x64f2b82800000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005,是不是abi.decode的用法错了?
合约代码
pragma solidity = 0.8.4; error MyCustomError(uint256 value1, uint256 value2); contract Reverter { function revertMe(uint256 valueA, uint256 valueB) public { revert MyCustomError(valueA, valueB); } } contract RevertCaller { Reverter reverter; event Revert(uint256 indexed value1, uint256 indexed value2); event LogBytes(bytes); constructor() { reverter = new Reverter(); } function callRevert(uint256 valueA, uint256 valueB) public { try reverter.revertMe(valueA, valueB) { } catch (bytes memory reason) { emit Revert(valueA, valueB); emit LogBytes(reason); (bytes4 errorSelector, uint256 value1, uint256 value2) = abi.decode(reason, (bytes4, uint256, uint256)); if (errorSelector == MyCustomError.selector) { // Handle invalid amount error emit Revert(value1, value2); } else { emit Revert(0, 0); } } } }
问题原因与解决方法
你的问题出在abi.decode的使用方式上:
- 自定义错误的编码格式是4字节selector + 按ABI编码的参数,日志里的
0x64f2b828是MyCustomError的selector,后面两段32字节数据是两个uint256参数。 - 直接用
abi.decode(reason, (bytes4, uint256, uint256))时,Solidity会尝试把整个reason拆成三个独立的ABI编码值,但实际上整个reason是一个完整的错误编码(selector+参数),并非三个单独编码的变量,因此解码失败导致回退。
有两种可靠的解决方式:
方式1:直接捕获自定义错误(推荐)
Solidity 0.8.4支持直接在catch块中指定自定义错误类型,无需手动解码,代码更简洁安全:
function callRevert(uint256 valueA, uint256 valueB) public { try reverter.revertMe(valueA, valueB) { } catch MyCustomError(uint256 value1, uint256 value2) { emit Revert(valueA, valueB); emit Revert(value1, value2); } catch (bytes memory reason) { // 处理其他类型的错误 emit Revert(0, 0); } }
方式2:手动切片解码参数
如果必须使用catch(bytes)的方式,需要先提取selector,再对selector之后的bytes片段解码参数:
function callRevert(uint256 valueA, uint256 valueB) public { try reverter.revertMe(valueA, valueB) { } catch (bytes memory reason) { emit Revert(valueA, valueB); emit LogBytes(reason); bytes4 errorSelector = bytes4(reason); if (errorSelector == MyCustomError.selector) { // 从第4字节开始切片,解码参数 (uint256 value1, uint256 value2) = abi.decode(reason[4:], (uint256, uint256)); emit Revert(value1, value2); } else { emit Revert(0, 0); } } }
以上两种方式都能正确获取自定义错误的参数,避免解码回退的问题。
内容的提问来源于stack exchange,提问作者jjordan
相关产品推荐
相关产品推荐

