21

We're trying to figure out whether to use return or throw in Solidity when a condition fails and we don't assume user maliciousness. Here's the pros and cons we've figured so far:

Why use throw

  • Any side effects of the code are reverted
  • Some wallets may predict throw ahead of time, warning the user

Why use return

  • Less gas is consumed by the unsuspecting user (again, assuming not a malicious call).
  • A calling contract may gracefully recover from the failure, unlike with throw.

Are there any other considerations (or best-practices) we should be aware of?

9

All the considerations in the question are helpful.

throw is safer since it ensures that there are no side effects that remain, but another consideration is error reporting. With throw there's currently no way to get more information about the error whereas Events and error codes could be used with return to convey a more granular reason about the error.

Another thing to be aware of when using web3.js, is that when a Solidity throw is encountered, currently web3.js has an issue with false values.

EDIT: Since throw consumes all gas, keep an eye on EIP 140 REVERT instruction:

The REVERT instruction provides a way to stop execution and revert state changes, without consuming all provided gas and with the ability to return a reason.

  • Be careful because throw doesn't return the sent ether, it only rollbacks changes to data. – Pablo Yabo Jun 7 '17 at 15:47
  • 3
    @PabloYabo Correct if you meant that throw consumes all gas and I added the clarification. Otherwise, an ether transfer is reverted with either a throw or revert. – eth Jun 11 '17 at 8:11
  • What I mean is that if you call send function to send Ether to another account, it doesn't rollback the sent ether with a throw. – Pablo Yabo Jun 12 '17 at 13:36
  • 1
    @PabloYabo A throw does rollback the sent ether: ethereum.stackexchange.com/questions/2428/… – eth Jun 23 '17 at 5:39
  • the throw returns sent ether but it doesn't rollback ether sent within the executed code. It returns the msg.value but only that. If you sent ether in your code, it isn't rolled back. – Pablo Yabo Jun 23 '17 at 20:26
3

You may also want to consider why not to use throw / return:

Why not use throw

  • All gas is consumed, everything reverted
  • Impossible to tell callers why the call failed
  • Errors cannot be caught, so they cannot be handled gracefully

Why not use return

  • It may not be clear to callers that the function can unsuccessfully run. This can lead to disastrous consequences: contract.doSomeImportantCall() may return false, but the caller instead expected a throw, so unless they check the return value bad things can happen.
  • You will find yourself returning multiple values everywhere: eg returns (bool _successful, uint _result) vs returns (uint _result)
  • Handling multiple return values is cumbersome and more difficult to test against.

There really is no consensus on the proper way to do things. In fact, there is currently a debate over throw vs return for transfer on the updated ERC20 spec, for example.

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.