Difference between require and assert and the difference between revert and throw

  • I was looking at the docs and Im 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?

  • maurelian

    maurelian Correct answer

    4 years ago

    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.

    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 ?

    Both rollback changes, which is indicated in the Gas Efficiency section.

    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?

    The difference is also semantic, ie. "this condition shouldn't even be reachable. Try compiling this code with a recent version of `solc`: https://gist.github.com/maurelian/02904ae729fb11213cde20ba05a202e6 It will warn you that it's possible for the second assert statement to be true for certain values.

License under CC-BY-SA with attribution

Content dated before 7/24/2021 11:53 AM