Solidity

Solidity Error Handling

Solidity Error Handling Main Tips

  • Solidity utilizes exceptions that can revert state for error handling, undoing all changes that have been made to the current call’s state, including all sub-calls, as well as flagging the error to the caller.
  • For convenience, when it comes to handling errors, you can use the functions assert and require for checking the conditions and throwing an exception if the specified condition is not met.

Solidity Error Handling

Solidity utilizes exceptions that can revert state for error handling, undoing all changes that have been made to the current call’s state, including all sub-calls, as well as flagging the error to the caller.For convenience, when it comes to handling errors, you can use the functions assert and require for checking the conditions and throwing an exception if the specified condition is not met.

The function assert should be used only for testing for internal errors, and checking invariants. The function require should be called for ensuring valid conditions, for example, contract state variables, or inputs, have beem met, or for validating return values from calls to external contracts. If properly used, the analysis tools may evaluate your contract in order to identify the function calls and conditions, which will reach a failing assert function. Code that functions properly should never reach an assert statement that is failing; in case this happens, there must be a bug in your contract that requires immediate attention.

Exceptions may be triggered in two ways: The function revert is used for flagging an error and reverting the current call. Later on, it may become possible to include error details as well in a call to revert. The throw keyword works as an alternative to revert() as well.

In the scenario that exceptions occur in a sub-call, they would “bubble up” (meaning that exceptions are rethrown) automatically. There are exceptions to this rule though – send and functions that are considered low-level, such as delegatecallcall and callcode – will return false when an exception occurs instead of “bubbling up”.

As of now, exceptions cannot be caught.

In the example below, it’s shown how require may be used for easily checking input conditions and how assert is used for internal error checking:

Example

pragma solidity ^0.4.0;

contract TheSharer {
    function sendHalfSum(address addrTo) payable returns (uint balance) {
        require(msg.value % 2 == 0); // Allow even numbers only
        uint balanceBefore = this.balance;
        addrTo.transfer(msg.value / 2);
        // Because transfer will throw an exception if it fails and
        // is unable to call back here, we should have no way to
        // = have half of all the money still.
        assert(this.balance == balanceBefore - msg.value / 2);
        return this.balance;
    }
}

 

Try on Remix Try live on Hosting

assert-style exceptions are generated in these situations:

  1. When an array at a too large or negative index is accessed (i.e. x[i] where i >= x.length or i < 0).
  2. When a fixed-length bytesN at a too large or negative index is accessed.
  3. When you modulo or divide by zero (e.g. 5 / 0 or 23 % 0).
  4. When shifting by a negative amount.
  5. When a value that is too big or negative is converted into an enum type.
  6. When a zero-initialized variable of internal function type is called.
  7. When assert with an argument which evaluates to false is called.

require-style exceptions are generated in these situations:

  1. When calling throw.
  2. When calling require with an argument which will evaluate to false.
  3. When you a function is called using a message call but does not finish properly (i.e. running out of gas, having no matching function, or throwing an exception itself), with the exception of low level, operations such as delegatecallcall or callcode, and send, being used. These low level operations do not throw throw exceptions, only indicating failures by returning false.
  4. When a contract is created using the keyword new but the creation of the contract would not finish properly (the definition of “not finishing properly” shown above).
  5. When performing an external function call that targets a contract which would contain no code.
  6. When your contract has received Ether through a public function that has no payable modifier (includes the fallback functions and the constructor).
  7. When your contract has received Ether through a public getter function.
  8. When .transfer() fails.

Internally, Solidity would perform a revert operation (instruction 0xfd) for an exception that is require-style and execute an invalid operation (instruction 0fxe) to throw another exception, in the assert-style this time. In either case, this would cause the EVM to revert any changes that have been made to the state. This is done because there is no secure way for the execution to continue since what occurred was not the expected effect. Since we would like to retain the transactions’ atomicity, reverting all changes and making the whole transaction (or at least call) without effect would be the safest thing to do. Keep in mind that assert-style exceptions will consume all gas that the call has available to it, meanwhile, require-style exceptions would not consume any gas ever since the Metropolis release.

Note: Ever since version 0.4.13 the keyword throw has become deprecated and planned for removal in the future.

Warning: The low-level operations such as delegatecall, call and callcode will only return success when the account that is calling does not exist, as a part of the EVM’s design. Existence has to be checked before calling if preferred.

Read previous post:
Solidity Scoping And Declarations

Solidity Scoping And Declarations Main Tips In Solidity variables only get scoped up to their declaration on the end of the semantic block. Additionally, declared variables are...

Close