104

I was looking at the docs and I'm looking for clarification on the difference between require and assert and throw and revert.

assert(bool condition): abort execution and revert state changes if condition is false (use for internal error)

require(bool condition): abort execution and revert state changes if condition is false (use for malformed input)

Specifically with respect to assert and require, how do you draw the line between malformed input and internal error?

1

5 Answers 5

127

edit 2022-12-08: As of Solidity 0.8.0 this answer is no longer accurate with respect to opcode and gas usage.

Furthermore, internal errors like division by zero, assertion failure and others do not use the invalid opcode anymore, but use revert with a special error message, in order to not waste gas in such situations.

However the guidance around when to use which remains true, ie. you only really want to use assert for analysis, it's basically a PANIC now.


There are two aspects to consider when choosing between assert() and require()

  1. Gas efficiency
  2. Bytecode analysis

1. Gas efficiency

assert(false) compiles to 0xfe, which is an invalid opcode, using up all remaining gas, and reverting all changes.

require(false) compiles to 0xfd which is the REVERT opcode, meaning it will refund the remaining gas. The opcode can also return a value (useful for debugging), but I don't believe that is supported in Solidity as of this moment. (2017-11-21)

2. Bytecode analysis

From the docs (emphasis mine)

The require function should be used to ensure valid conditions, such as inputs, or contract state variables are met, or to validate return values from calls to external contracts. If used properly, analysis tools can evaluate your contract to identify the conditions and function calls which will reach a failing assert. Properly functioning code should never reach a failing assert statement; if this happens there is a bug in your contract which you should fix.

The above excerpt is a reference to the still (as of 2017-11-21) experimental and undocumented SMTChecker.

I use a few heuristics to help me decide which to use.

Use require() to:

  • Validate user inputs
  • Validate the response from an external contract
    ie. use require(external.send(amount))
  • Validate state conditions prior to executing state changing operations, for example in an owned contract situation
  • Generally, you should use require more often,
  • Generally, it will be used towards the beginning of a function.

Use assert() to:

  • check for overflow/underflow
  • check invariants
  • validate contract state after making changes
  • avoid conditions which should never, ever be possible.
  • Generally, you should use assert less often
  • Generally, it will be use towards the end of your function.

Basically, assert is just there to prevent anything really bad from happening, but it shouldn't be possible for the condition to evaluate to false.

Historical note:

The require() and assert() functions were added to Solidity prior to the Byzantium fork, in v0.4.10. Prior to Byzantium, they behaved identically, but already compiled to different opcodes. This meant that some contracts deployed before Byzantium behaved differently after the fork, the main difference being that began refunding unused gas.

4
  • But both assert() and require() rollback change written to the blockchain so that balance[_to] =balance[_from] +_value would be reverted if assert() or require() condition is triggered isn’t it ? (I’m talking after last year's hardfork). Or does assert() keep change being made ? May 25, 2018 at 10:31
  • Both rollback changes, which is indicated in the Gas Efficiency section.
    – maurelian
    May 25, 2018 at 15:56
  • 7
    I don't really understand the point of using assert anymore since I have the exact same behavior with require but I have the chance of refunding some gas, what's the purpose?
    – Jayr Motta
    Jul 4, 2018 at 19:28
  • The difference is also semantic, ie. "this condition shouldn't even be reachable. Try compiling this code with a recent version of solc: gist.github.com/maurelian/02904ae729fb11213cde20ba05a202e6 It will warn you that it's possible for the second assert statement to be true for certain values.
    – maurelian
    Jul 7, 2018 at 17:11
25

I'm using require for input validation as it's a little more efficient that if/throw.

function foo(uint amount) {
    require(amount < totalAmount);
    ...
}

Where as assert should be used more for runtime error catching:

function foo(uint amount) {
    ...
    __check = myAmount;
        myAmount -= amount;
    assert(myAmount < __check);
    ...
}

revert will revert changes and refund unused gas in a later version of Ethereum but ATM acts in the same way as throw.

3
  • Could I also just write assert(myAmount-amount<myAmount) and skip the __check variable? which one is better, why/why not? Nov 28, 2017 at 15:00
  • Much safer not to have unnecessary duplication of logic in the state mutation and the assert argument. To do so could lead to a debugging nightmare where the operations were chained for state but forgotten for the assert.
    – o0ragman0o
    Nov 29, 2017 at 3:10
  • But both assert() and require() rollback change written to the blockchain so that balance[_to] =balance[_from] +_value would be reverted if assert() or require() condition is triggered isn’t it ? (I’m talking after last year's hardfork). Or does assert() keep change being made ? But both assert() and require() rollback change written to the blockchain so that balance[_to] =balance[_from] +_value would be reverted if assert() or require() condition is triggered isn’t it ? (I’m talking after last year's hardfork). Or does assert() keep change being made ? May 25, 2018 at 10:32
19

I think none of the answers is correct.

assert is reserved for conditions in which it is expected that static code analysis tools (maybe Solidity compiler in future versions) will be able to detect the error warning the developer at compile time.

require is reserved for error-conditions of incorrect input data to functions (when compared to expected/valid input data) that can not be detected until execution time. This correspond to function preconditions in programming language argot. The compiler is unable to help due to the infinite possibilities of input data.

throw is deprecated in favor of revert.

revert is reserved for error-conditions that affect business-logic. For example someone sends a vote when the voting is already closed.

require and revert are mostly similar respect to the internal EVM implementation, but developers will appreciate the distinction.

1
  • 1
    Very updated answer. +1
    – Pe Dro
    Apr 24, 2021 at 23:25
3

Assert is suitable for checking conditions which are not meant to occur but do.

Require is suitable for checking for unwanted conditions which can occur.

1

Solidity has an SMTChecker which makes using assert very cool because it can prove that your invariants are true:

Solidity implements a formal verification approach based on SMT (Satisfiability Modulo Theories) and Horn solving. The SMTChecker module automatically tries to prove that the code satisfies the specification given by require and assert statements. That is, it considers require statements as assumptions and tries to prove that the conditions inside assert statements are always true. If an assertion failure is found, a counterexample may be given to the user showing how the assertion can be violated. If no warning is given by the SMTChecker for a property, it means that the property is safe.

You should use assert to help you find invariants which have been violated.

You use assert to help you catch when the impossible happens.

The SMTChecker Tutorial provides a good example of assert that is too lengthy to include here: highly recommend to read it.

If the SMTChecker gets you excited to start using assert, that's a very good thing. But remember they are for invariants, as the Solidity docs mention:

Assert should only be used to test for internal errors, and to check invariants. Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix. Language analysis tools can evaluate your contract to identify the conditions and function calls which will cause a Panic.

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.