5

My goal is to carry out some simple function but the user should be aware if the call fails. An example simple function could be seen here.

Instead of return false, I want to use throw. Would their effect be the same on the function's flow? And which one uses less gas?

Overall, which one might be more motivated to use?

3

This discussion mostly matters in the context of a contract calling another contract. If the callee contract throws there is no catch in the caller contract in current EVM versions. Thus, throw error is impossible to recover if needed. But this is rarely needed in usual use cases.

Using throw make it easier to see if the transaction had any error in a blockchain explorer.

See also discussion of upcoming features addressing these https://github.com/ethereum/EIPs/issues/140

  • "Using throw make it easier to see if the transaction had any error in a blockchain explorer.". The irony is that if you like doing things programmatically (i.e. no human in the loop), throw is HARDER to diagnose because all you get is gasUsed = gasSent, you get no error message and you can't figure out which line of code failed in which contract. – Paul S Apr 12 '17 at 15:52
  • @PaulS: Transactions do not have return value either. – Mikko Ohtamaa Apr 12 '17 at 16:56
  • transactions can log messages, which is how I do automated testing. (and/or write accessor functions). An aborted transaction logs nothing... – Paul S Apr 12 '17 at 17:17
5

Just thought I would chime in, in case this Q&A is useful to others. For brevity, just imagine I prefixed everything with in my opinion.

It's a very general question. I would segregate the situations into two cases:

  1. "No" is a valid answer to a question
  2. Something is wrong with the transaction

In the first case, return false might be the way to go. For example, "Is it Monday?" - No. Nothing is especially wrong and "No" is a valid and expected response. Implicitly, the caller should be prepared for the possibility that "No" will come back and deal with it accordingly.

A great number of cases fall into the second category; something is wrong. In these cases it's almost always preferable to throw; in my opinion.

Consider that we can deploy a contract and then in the future the caller might be another contract. If we return false we are, in effect, burdening the caller with the responsibility of contending with unexpected results. That leads to complexity and complexity is something we don't want in Smart Contracts. On the other hand, if we throw at the first sign of trouble, we're doing a service to all future callers; if our function "didn't happen" then nothing happened.

This is quite the opposite of the philosophy in other environments where there is a near obsession with catching errors and explaining problems. I think it deserves mentioning for those starting out and possibly wondering about the customary approach.

In a Smart Contract, I tend to fail early, fail hard and explain nothing. In other words, throw at (just about) every opportunity.

General heuristic:

  1. Validate input and throw if something is wrong.
  2. Do stuff.
  3. Check results and if something is wrong, throw.
  4. Return "success" or result.

It might seem a little brutal. A Smart Contract should in my opinion focus on safeguarding application (and data) integrity and it should do so in the simplest way possible because we're dealing with a platform on which an error or oversight could have non-trivial consequences and could be difficult or impossible to fix.

We're aiming for "obviously correct". Minimizing complexity implies that explaining the reasons is a separate concern (for clients to figure out). Throw is usually the surest and simplest response to exceptions that threaten the integrity of the system.

Hope it helps.

2

Generally, YES it is better to use throw instead of return false.

An objective reason is that throw is safer because it reverts all state changes: it's certain that when the call fails that nothing changes. It can be possible to manually track and undo all changes, but it's possible to miss something by doing it "manually".

A subjective reason is that correctness, security, and safety are the most important priorities for decentralized, trustless applications (like @Rob describes), and throw is objectively safer.

Throw vs. Return describes:

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.

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.


In the future

revert will likely be the one to use since it will have all the benefits of throw and return:

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.

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.