|
1 | 1 | package testutil |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "context" |
| 6 | + "fmt" |
5 | 7 | "time" |
6 | 8 |
|
| 9 | + "github.com/ethereum/go-ethereum/accounts/abi" |
| 10 | + "github.com/ethereum/go-ethereum/common/hexutil" |
| 11 | + |
7 | 12 | "github.com/cosmos/evm/crypto/ethsecp256k1" |
| 13 | + evmtypes "github.com/cosmos/evm/x/vm/types" |
| 14 | + |
| 15 | + errorsmod "cosmossdk.io/errors" |
8 | 16 |
|
9 | 17 | "github.com/cosmos/cosmos-sdk/client" |
10 | 18 | "github.com/cosmos/cosmos-sdk/client/tx" |
@@ -108,3 +116,39 @@ func CreateTx(ctx context.Context, txCfg client.TxConfig, priv cryptotypes.PrivK |
108 | 116 |
|
109 | 117 | return txBuilder.GetTx(), nil |
110 | 118 | } |
| 119 | + |
| 120 | +// DecodeRevertReason extracts and decodes the human-readable revert reason from an EVM transaction response. |
| 121 | +// It processes the raw return data (Ret field) from a failed EVM transaction and attempts to decode |
| 122 | +// any ABI-encoded revert messages into readable error strings. |
| 123 | +// |
| 124 | +// Returns: |
| 125 | +// - error: A formatted error containing either: |
| 126 | +// - "tx failed with VmError: <vmError>: <decoded_reason>" for successfully decoded reverts |
| 127 | +// - "tx failed with VmError: <vmError>: <hex_data>" for non-decodable data |
| 128 | +// - "failed to decode revert data: <decode_error>" if decoding fails |
| 129 | +// |
| 130 | +// Example usage: |
| 131 | +// |
| 132 | +// res, err := executeTransaction(...) |
| 133 | +// if res.VmError != "" { |
| 134 | +// decodedErr := DecodeRevertReason(res) |
| 135 | +// // decodedErr might be: "tx failed with VmError: execution reverted: ERC20: insufficient allowance" |
| 136 | +// } |
| 137 | +func DecodeRevertReason(evmRes evmtypes.MsgEthereumTxResponse) error { |
| 138 | + revertErr := evmtypes.NewExecErrorWithReason(evmRes.Ret) |
| 139 | + hexData, ok := revertErr.ErrorData().(string) |
| 140 | + if ok { |
| 141 | + decodedBytes, err := hexutil.Decode(hexData) |
| 142 | + if err == nil { |
| 143 | + if len(decodedBytes) >= 4 && bytes.Equal(decodedBytes[:4], evmtypes.RevertSelector) { |
| 144 | + var reason string |
| 145 | + reason, err = abi.UnpackRevert(decodedBytes) |
| 146 | + if err == nil { |
| 147 | + return fmt.Errorf("tx failed with VmError: %v: %s", evmRes.VmError, reason) |
| 148 | + } |
| 149 | + } |
| 150 | + } |
| 151 | + return errorsmod.Wrap(err, "failed to decode revert data") |
| 152 | + } |
| 153 | + return fmt.Errorf("tx failed with VmError: %v: %s", evmRes.VmError, revertErr.ErrorData()) |
| 154 | +} |
0 commit comments