3

Is there any way to catch solidity revert message like Remix Javascript VM in web3j

Revert message(remix)

transact to Erecruitment.issueNewAdmitCard errored: VM error: revert.
revert  The transaction has been reverted to the initial state.
Reason provided by the contract: "Admit card ID already exists in Blockchain".  Debug the transaction to get more information. 

Event message (remix)

 logs   [
    {
        "from": "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a",
        "topic": "0xbf970614f4ff9483c34fa1a053bc6614e06003a1faef2d0876e9255bfa3167fc",
        "event": "LogIssueNewCard",
        "args": {
            "0": "2",
            "1": "4",
            "2": "New Card is issued in Blockchain",
            "admitCardId": "2",
            "applicantName": "4",
            "message": "New Card is issued in Blockchain",
            "length": 3
        }
    }
]

I get event message for successful transaction in web3j. So, I can be sure that whether my transaction is completed without any error . If transaction is reverted there will be no event as usual(it's ok). But I am not getting any revert message too for failed transaction.

And I am getting transaction status null each and every time using web3j as byzantiumBlock is missing in my genesis file. Then how can I check the transaction status?

1

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)".

You'll need to make an eth_call to your contract.

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();
}
0

I'm not sure that getting the revert message is possible with web3j, however I get the status of my transactions with this command (but ofc you should wait until the transaction is mined and your Node is synchronized)

EthGetTransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt("The Hash of your Transaction").send();
    if (transactionReceipt.getResult() != null && !transactionReceipt.hasError()) {
        System.out.println(transactionReceipt.getTransactionReceipt().get().getStatus());
    } else {
      "TRY AGAIN SOMEHOW LATER.. :-)"
    }

For your question about events, I think in the generated smart contract wrappers there is event methods that you can easily use. see : https://docs.web3j.io/smart_contracts.html

  • I appreciate your answer but you will get result for every mined transaction but may not get result field ( as it depends on byzantiumBlock field in genesis file). I agree that events may help you but event will not be triggered unless it is executed that is execution reached to that line. what do your think? – sharif2008 Nov 30 '18 at 6:05
  • Yes with events you can't be sure if your transaction failed or just stuck. I have not tried it yet, but maybe you can somehow use a FailedEvent that got emitted when (Admit card ID already exists in Blockchain), and undo what happend before that emit. – Majd TL Nov 30 '18 at 9:19
  • @sharif2008 maybe that could help somehow: I was using Ganache with automine then this code gave me the revert message EthSendTransaction transactionTx = web3j.ethSendRawTransaction(sigendTransactionData).sendAsync().get(); if (transactionTx.hasError()) { System.out.println(transactionTx.getError().getMessage() ); } – Majd TL Dec 15 '18 at 10:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.