[Ethereum] How to receive revert() reason for past transactions

evmgeth-debuggingparityrevert-opcodesolidity

I run a geth/parity nodes for broadcasting user's transactions to Ethereum blockchain. Some of contract functions provide reasons for revert (see examble below):

contract Example {
  function test (uint i) {
    require(i == 1, "ERROR_CODE")
  }
}

As I understand it is not possible to get revert reason string with eth_getTransactionReceipt. However I still need to get revert reason for failed transactions.

How to get revert reason ("ERROR_CODE" in example above) for past failed transactions? (at least for transactions in last 20 blocks)

Best Answer

In solidity 0.4.22 the require and revert reason were added. As can be seen here, they are abi-encoded as if it were a call to a function "Error(string)".

This blog post gives an example: an eth_call to a function

function myFunction(uint256 input) public view returns (uint256) {
    require(input >= 5, "myFunction only accepts arguments which are greather than or equal to 5");
    return input * input - 25;
}

with an invalid input argument (less than 5 in this example), will return

0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000476d7946756e6374696f6e206f6e6c79206163636570747320617267756d656e747320776869636820617265206772656174686572207468616e206f7220657175616c20746f203500000000000000000000000000000000000000000000000000

which is

0x08c379a0                                                       // Function selector
0000000000000000000000000000000000000000000000000000000000000020 // Offset of string return value
0000000000000000000000000000000000000000000000000000000000000047 // Length of string return value (the revert reason)
6d7946756e6374696f6e206f6e6c79206163636570747320617267756d656e74 // first 32 bytes of the revert reason
7320776869636820617265206772656174686572207468616e206f7220657175 // next 32 bytes of the revert reason
616c20746f203500000000000000000000000000000000000000000000000000 // last 7 bytes of the revert reason

So decoding the returned string will give you the revert reason.

With Web3j this could be done like:

public Optional<String> getRevertReason(EthCall ethCall) {
    String errorMethodId = "0x08c379a0"; // Numeric.toHexString(Hash.sha3("Error(string)".getBytes())).substring(0, 10)
    List<TypeReference<Type>> revertReasonTypes = Collections.singletonList(TypeReference.create((Class<Type>) AbiTypes.getType("string")));

    if (!ethCall.hasError() && ethCall.getValue() != null && ethCall.getValue().startsWith(errorMethodId)) {
        String encodedRevertReason = ethCall.getValue().substring(errorMethodId.length());
        List<Type> decoded = FunctionReturnDecoder.decode(encodedRevertReason, revertReasonTypes);
        Utf8String decodedRevertReason = (Utf8String) decoded.get(0);
        return Optional.of(decodedRevertReason.getValue());
    }
    return Optional.empty();
}
Related Topic