ETHEREUM SMART CONTRACTS DEVELOPMENT
Solidity Fundamentals: Error Handling
Assert, Require, Revert, and Exceptions
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 showingfalse
- 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 type — Error(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 forassert
.
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
orstaticcall
is used. The low level operations never throw exceptions but indicate failures by returningfalse
(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 userequire
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 types — internal
, external
, public
and private
. Thanks for reading.