6

In my contract, I have a bunch of checking at the start of a public function. It looks something like this:

require ( prx < 1e18, "Value prx too high")
require ( prx != 0, "Value prx cannot be zero")
if (!validAmt(amt)) { revert("Value amt invalid"); }

Obviously I would like the user to see the failure and the reason text, is this possible in web3.js, or perhaps web3.py?

I know I can capture events, but I don't want to log events on these kind of failures.

3 Answers 3

3

Sadly there's no easy way to do it on a generalized, transaction level. If you call a contract from a contract, you can catch the reason within the calling contract. Then you would log an event log LogError(string) to log the reason it found.

However, if you need to revert the root call (that is, the very first call the EOA makes to the contract), the only way is to run a parity debug trace on the transaction to get the revert reason.

3
  • Really, I thought revert killed the whole EOA generated transaction -- are you saying if I'm in contract A and I call B.f() and in that call it reverts, this can be caught in A? How? Commented Feb 25, 2019 at 10:32
  • 1
    At the EVM level, there's no inherent bubbling of a failure. When a call frame created by a CALL, DELEGATECALL, etc. fails, it just pushes 1 to the stack. The calling contract handles the failure however it wants. The Solidity compiler automatically adds a check to these that bubbles up the failures. The way around this is to call the call function of an address, passing in the function name and parameters. The call function will just return the pass/fail boolean instead of reverting.
    – natewelch_
    Commented Feb 26, 2019 at 12:44
  • 1
    To add, you can grab the revert reason after the failure by dropping into Solidity Assembly.
    – natewelch_
    Commented Feb 26, 2019 at 12:45
3

In your web3 code, you can use a try/catch block, and parse the thrown error:

try {
  const receipt = await web3.methods.foo().send(...);
  ...
} catch (e) {
  //parse e here, the reason will be inside
}
1

I made an NPM package to help with this: eth-revert-reason


It is hard to decode the revert reason in a generalized manner. Many different factors, such as web3.js vs. ethers, Geth vs. Parity, etc. will result in different results for all the answers posted here. Some issues are:

  1. For a Kovan transaction, you need a custom provider that exposes Parity trace methods.
  2. Transactions may result in different messages depending on the context of the block they are called from. Because of this, you may need to be running a full-archival node in order to retrieve the correct error message.

In a happy-path case, the code to retrieve the revert reason is:

const provider = customProvider || ethers.getDefaultProvider(network)
const tx = await provider.getTransaction(txHash)
const code = await provider.call(tx)

While the code to generate the revert reason in the normal case is simple, the non-happy-path cases are surprisingly difficult to handle, however this package attempts to appease this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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