You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:55:30