ETHEREUM SMART CONTRACTS DEVELOPMENT

Solidity Fundamentals: Error Handling

Assert, Require, Revert, and Exceptions

Ferdi Kurt
Coinmonks
Published in
4 min readJan 13, 2021

--

Reverting state changes to prevent possible issues is one necessity when writing Solidity. In this part we will be working on how to handle errors using revert, require, assert and try/catch to undoes all changes made to the state in the current call.

Currently, Solidity supports two error signatures: Error(string) and Panic(uint256). Error(string) is used for regular error conditions while Panic(uint256) is used for errors that should not be present in bug-free code.

Panic via assert

assert should only be used to test for internal errors, and to check invariants. It also should return true since failing assertion means there is a bug inside the code. Following are the cases when Solidity creates assert type — Panic(uint256) exceptions;

  • Calling assert with an argument showing false
  • Provided arithmetic operation results in underflow or overflow (see: Value Types: Part One)
  • Divide or modulo by zero
  • Convert too big or negative value to enum
  • Calling pop() on an empty array
  • Access an array element which is out of bounds or negative
  • Allocate to much memory or create an array that is too large
  • Invoking zero-initialized variable of an internal function type

Error via require

The require function used to guarantees valid conditions that can’t be detected before execution. It checks conditions on input, contract state variables or return values from calls to external contracts. It either creates an error type of Error(string) or an error without any error data. Following are the cases when Solidity creates require typeError(string) exceptions;

  • Calling require with an argument showing false
  • Performing an external function call to a contract that contains no code
  • When contract receives Ether via a public function without payable modifier — including constructor and fallback function (more on these topics later)
  • When your contract gets Ether through a public getter function.

remember: You can optionally provide a message string for require, but not for assert.

For the following cases, the error data from the external call (if provided) is forwarded. This mean that it can either cause an Error or a Panic (or whatever else was given):

  • When transfer() fails
  • When calling a function via a message call but it does not finish properly (i.e., it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation call, send, delegatecall, callcode or staticcall is used. The low level operations never throw exceptions but indicate failures by returning false (more on low level function calls later).
  • When create a contract using the new keyword but the contract creation does not finish properly.

Note: Panic exceptions used to use the invalid opcode before Solidity 0.8.0, which consumed all gas available to the call. Exceptions that use require used to consume all gas until before the Metropolis release.

revert

For reverting current call, we can use Solidity revert function to generate exceptions for displaying error. This function will create an Error(String) exception which optionally takes string message containing details about the error.

If we provide the error string directly, then above two syntax options are equivalent, we can choose either.

Above example, revert(“Only exact payments!”) returns the following hexadecimal as error return data:

try/catch

Exceptions during contract creation and external calls can be caught with the try/catch statement. try keyword has to be followed by an expression to an external function call or a contract creation (new ContractName()).

The error which was occur after by revert("reasonString") or require(false, "reasonString") resulted with the catch clause of the type catch Error(string memory reason) invocation.

catch (bytes memory lowLevelData) is executed if the error signature does not match any other clause, if there was an error while decoding the error message, or if no error data was provided with the exception.

We can just use catch { … } if we are not interested in error data.

In order to catch all error cases, we have to have at least the clause catch { ...} or the clause catch (bytes memory lowLevelData) { ... }.

Next, we will be working on Solidity function typesinternal, external, public and private. Thanks for reading.

Feel free to ask any questions.

Stay safe, do good work, and keep in touch!

Ferdi Kurt

--

--