Errors
Maintaining Invariants
In an ideal world, we want our programs to contain the logic necessary to handle all cases. X occurred? No problem, there is some code to handle X. Y occurred? Not to worry, there is also some code to handle Y. However, we do not live in an ideal world, and below are two reasons as for why this is the case:
In certain situations, we cannot account for every single case, especially if the number of cases is of a large magnitude
In certain situations, we do not have a good solution for solving a problem our program is in
Fortunately, we can utilize the concept of errors to help navigate the flawed world that we live in. More specifically, we can use errors to respect invariants - properties of our programs (i.e. smart contracts) that we wish to maintain throughout its lifetime.
Raising Errors via require
require
For this section, we refer to Bank
which is a smart contract designed to act like a bank:
Assuming that deposit
is correct, we will focus on the withdraw
function; this function is designed to send the caller the desired amount of ether they wish to withdraw. Ideally, we want for withdraw
to maintain the following invariants of our Bank
contract:
Users cannot withdraw more than what they have in the bank
The bank successfully sends users their ether
As it currently stands, withdraw
does not respect the invariants listed above. However, we can utilize errors to revert the execution of withdraw
if either invariant is not respected. In particular, we will utilize the require
keyword to maintain both invariants.
Focusing on the first invariant, we want to check that the user is not withdrawing more than what they have in the bank. In terms of pseudocode, we want the following boolean condition to hold:
The require
keyword takes a boolean condition e
; if e
evaluates to true, nothing happens. Otherwise, if e
evaluates to false, the transaction reverts. Therefore, we want to incorporate the following code into our contract:
Furthermore, the require
keyword also accepts an optional error message. Updating our code to include an error message, we have the following updated contract:
At this point, withdraw
now respects the first invariant; but what about the second invariant? Recall from the contract interactions section that the success
variable in withdraw
indicates whether if the payment of ether was successful. Therefore, we just need to check that success
is equal to true. Updating our Bank
smart contract one final time, we have:
revert
and assert
revert
and assert
In addition to require
, we also have the keywords revert
and assert
which also allow us to raise errors in our smart contracts.
revert
acts very similarly to require
, except that no boolean condition is needed for revert
. An example of this can be seen below (the error message is optional, but recommended):
The last keyword we will discuss is assert
. assert
only takes in a boolean condition. However, what makes assert
special is that if it is ever called by a smart contract, all the gas remaining in said transaction is utilized! Whereas for require
and revert
refund any remaining gas.
Last updated