Require, Revert, Assert

Require is a keyword in Solidity that is used to make sure that functions are called with the correct arguments.

  • require declares constraints which must be satisfied before code execution.

  • It accepts a single argument and returns a boolean value after evaluation.

  • It also has a custom error string option.

contract ExampleContract {    
    function exampleFunction(uint a, uint b) public {        
        require(a > b);    
    }
}

If the condition evaluates to false then exception is raised and execution is terminated. The unused gas is returned back to the caller and the state is reversed to its original state.

Use require() to:

  • Validate function inputs

  • Validate the response from an external contract

  • Validate state conditions prior to executing state changing operations

Require and Revert

  • Same thing, user choice.

  • revert("some error string") aborts execution and reverts state changes, providing an explanatory string.

  • there has been a shift to using custom errors paired with revert due to gas savings.

Transactions and Errors

  • Errors are state-reverting

  • If an error is encountered everything that was done before is undone.

  • Entire transaction fails -> no state change is effected

Revert cascades

  • Say your smart contract sends a transaction to another smart contract.

  • An exception is thrown in the external contract.

  • As a result, all actions taken in both the external contract and your contract are reverted.

The error cascades as the entire flow of actions is treated as a single transaction. This applies to all high-level interactions/functions.

Low-level calls do not revert

  • Low-level calls: Address.send, address.call, address.delegatecall, address.staticcall

Example

Let’s say that you have a contract A which makes a low-level call or delegatecall to another contract B. The target contract B reverts with a revert message or custom error.

It’s important to know that a low-level call or delegatecall doesn’t revert while calling a function that reverts:

In the above example, when contract A calls contract B with a low-level call, The success variable signals whether the call was successful (true) or unsuccessful (false).

Using this, we could revert in the calling contract like so:

Use assert() to:

  • check for overflow/underflow

  • check invariants (states our contract or variables should never ever reach)

  • validate contract state after making changes

  • avoid conditions which should never, ever be possible.

  • Generally, you should use assert less often

Require and Assert

Both terminate and revert the transaction if some condition is not met. However, they have different compiled EVM bytecode:

  • require(false) compiles to 0xfd which is the REVERT opcode, meaning it will refund the remaining gas and revert all changes.

    • The opcode can also return a value (useful for debugging).

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

Last updated