Documentation for custom errors.

This commit is contained in:
chriseth
2021-03-30 23:04:32 +02:00
parent 15fe07bebe
commit 0c1be06cba
8 changed files with 241 additions and 30 deletions
+55 -24
View File
@@ -17,7 +17,7 @@ the usual semantics known from C or JavaScript.
Solidity also supports exception handling in the form of ``try``/``catch``-statements,
but only for :ref:`external function calls <external-function-calls>` and
contract creation calls.
contract creation calls. Errors can be created using the :ref:`revert statement <revert-statement>`.
Parentheses can *not* be omitted for conditionals, but curly braces can be omitted
around single-statement bodies.
@@ -551,7 +551,8 @@ state in the current call (and all its sub-calls) and
flags an error to the caller.
When exceptions happen in a sub-call, they "bubble up" (i.e.,
exceptions are rethrown) automatically. Exceptions to this rule are ``send``
exceptions are rethrown) automatically unless they are caught in
a ``try/catch`` statement. Exceptions to this rule are ``send``
and the low-level functions ``call``, ``delegatecall`` and
``staticcall``: they return ``false`` as their first return value in case
of an exception instead of "bubbling up".
@@ -562,17 +563,11 @@ of an exception instead of "bubbling up".
if the account called is non-existent, as part of the design
of the EVM. Account existence must be checked prior to calling if needed.
Exceptions in external calls can be caught with the ``try``/``catch`` statement.
Exceptions can contain data that is passed back to the caller.
This data consists of a 4-byte selector and subsequent :ref:`ABI-encoded<abi>` data.
The selector is computed in the same way as a function selector, i.e.,
the first four bytes of the keccak256-hash of a function
signature - in this case an error signature.
Currently, Solidity supports two error signatures: ``Error(string)``
and ``Panic(uint256)``. The first ("error") is used for "regular" error conditions
while the second ("panic") is used for errors that should not be present in bug-free code.
Exceptions can contain error data that is passed back to the caller
in the form of :ref:`error instances <errors>`.
The built-in errors ``Error(string)`` and ``Panic(uint256)`` are
used by special functions, as explained below. ``Error`` is used for "regular" error conditions
while ``Panic`` is used for errors that should not be present in bug-free code.
Panic via ``assert`` and Error via ``require``
----------------------------------------------
@@ -604,17 +599,24 @@ The error code supplied with the error data indicates the kind of panic.
#. 0x41: If you allocate too much memory or create an array that is too large.
#. 0x51: If you call a zero-initialized variable of internal function type.
The ``require`` function either creates an error of type ``Error(string)``
or an error without any error data and it
The ``require`` function either creates an error without any data or
an error of type ``Error(string)``. It
should be used to ensure valid conditions
that cannot be detected until execution time.
This includes conditions on inputs
or return values from calls to external contracts.
A ``Error(string)`` exception (or an exception without data) is generated
.. note::
It is currently not possible to use custom errors in combination
with ``require``. Please use ``if (!condition) revert CustomError();`` instead.
An ``Error(string)`` exception (or an exception without data) is generated
by the compiler
in the following situations:
#. Calling ``require`` with an argument that evaluates to ``false``.
#. Calling ``require(x)`` where ``x`` evaluates to ``false``.
#. If you use ``revert()`` or ``revert("description")``.
#. If you perform an external function call targeting a contract that contains no code.
#. If your contract receives Ether via a public function without
``payable`` modifier (including the constructor and the fallback function).
@@ -679,22 +681,43 @@ the changes in the caller will always be reverted.
which consumed all gas available to the call.
Exceptions that use ``require`` used to consume all gas until before the Metropolis release.
.. _revert-statement:
``revert``
----------
The ``revert`` function is another way to trigger exceptions from within other code blocks to flag an error and
revert the current call. The function takes an optional string
message containing details about the error that is passed back to the caller
and it will create an ``Error(string)`` exception.
A direct revert can be triggered using the ``revert`` statement and the ``revert`` function.
The following example shows how to use an error string together with ``revert`` and the equivalent ``require``:
The ``revert`` statement takes a custom error as direct argument without parentheses:
revert CustomError(arg1, arg2);
For backards-compatibility reasons, there is also the ``revert()`` function, which uses parentheses
and accepts a string:
revert();
revert("description");
The error data will be passed back to the caller and can be caught there.
Using ``revert()`` causes a revert without any error data while ``revert("description")``
will create an ``Error(string)`` error.
Using a custom error instance will usually be much cheaper than a string description,
because you can use the name of the error to describe it, which is encoded in only
four bytes. A longer description can be supplied via NatSpec which does not incur
any costs.
The following example shows how to use an error string and a custom error instance
together with ``revert`` and the equivalent ``require``:
::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
pragma solidity ^0.8.4;
contract VendingMachine {
address owner;
error Unauthorized();
function buy(uint amount) public payable {
if (amount > msg.value / 2 ether)
revert("Not enough Ether provided.");
@@ -705,9 +728,17 @@ The following example shows how to use an error string together with ``revert``
);
// Perform the purchase.
}
function withdraw() public {
if (msg.sender != owner)
revert Unauthorized();
payable(msg.sender).transfer(address(this).balance);
}
}
If you provide the reason string directly, then the two syntax options are equivalent, it is the developer's preference which one to use.
The two ways ``if (!condition) revert(...);`` and ``require(condition, ...);`` are
equivalent as long as the arguments to ``revert`` and ``require`` do not have side-effects,
for example if they are just strings.
.. note::
The ``require`` function is evaluated just as any other function.