Loading...

Error Handling in Solidity: Assert, Require, Revert, Exceptions, Try/Catch

question solidity ethereum
Ram Patra Published on August 18, 2024

Error handling in Solidity is a crucial aspect of writing secure and robust smart contracts. Solidity provides various mechanisms to handle errors and exceptions, ensuring that contracts behave predictably even when something goes wrong. The key components of error handling in Solidity include assert, require, revert, try/catch, and built-in error types like Error and Panic.

1. assert Statement

  • Purpose: assert is used to check for conditions that should never be false. If an assert condition fails, it indicates a bug in the contract.
  • Behavior: When an assert fails, it triggers a Panic error and consumes all the remaining gas, causing the transaction to revert.
  • Use Cases: Used for internal errors and invariant checks.
   function safeMath(uint a, uint b) public pure returns (uint) {
       uint result = a + b;
       assert(result >= a); // Check for overflow
       return result;
   }
  • Example Failure: Out-of-bounds array access, overflow/underflow in arithmetic operations in checked mode.

2. require Statement

  • Purpose: require is used to validate inputs, outputs, and conditions that could be invalid due to external factors like user inputs.
  • Behavior: If the condition in require fails, it reverts the transaction and refunds the remaining gas.
  • Use Cases: Input validation, checking return values from calls, ensuring certain conditions are met before executing further logic.
   function transfer(address recipient, uint amount) public {
       require(balance[msg.sender] >= amount, "Insufficient balance");
       balance[msg.sender] -= amount;
       balance[recipient] += amount;
   }
  • Example Failure: Insufficient balance, invalid function parameters.
  • Note: The require function is evaluated just as any other function. This means that all arguments are evaluated before the function itself is executed. In particular, in require(condition, f()) the function f is executed even if condition is true.

3. revert Statement

  • Purpose: revert is used to explicitly revert the transaction and provide an error message. It’s commonly used within if conditions to exit early and roll back state changes.
  • Behavior: Stops execution and reverts the transaction, returning an optional error message.
  • Use Cases: When more complex conditions or error messages are needed.
   function withdraw(uint amount) public {
       if (balance[msg.sender] < amount) {
           revert("Insufficient balance to withdraw");
       }
       balance[msg.sender] -= amount;
       payable(msg.sender).transfer(amount);
   }

4. try/catch Statement

  • Purpose: try/catch is used to handle errors in external function calls and contract interactions. It allows you to catch and handle exceptions from other contracts or internal functions that use try.
  • Behavior: Executes the try block, and if an error occurs, the control flow is transferred to the catch block.
  • Use Cases: Safe interaction with external contracts, managing failed external calls, dealing with low-level errors.
   error CustomError(string description);

   function callExternal(address target, uint amount) public {
       try IExternalContract(target).someFunction{value: amount}() {
           // Handle success
       } catch CustomError(string _desc) {
           // Handle CustomError
           revert CustomError(_desc);
       } catch {
           // This is a catch-all block but this does not capture any error data
           revert("External call failed");
       } catch(bytes memory lowLevelData) {
           // This is a catch-all block that captures the error data
           revert(lowLevelData);
       }
   }

5. Error Types: Error and Panic

Solidity defines two built-in error types: Error and Panic.

  • Error(string): Thrown when a require, revert, or invalid opcode is encountered. It indicates a recoverable error.
    • Use Cases: Failed require checks, user-defined errors.
  • Panic(uint256): Thrown by assert or due to internal Solidity issues, such as arithmetic overflows (in checked mode) or out-of-bounds array access.
    • Use Cases: Indicates a bug or an unrecoverable error in the contract.
    • Panic Error Codes:
      • 0x00: Used for generic compiler inserted panics.
      • 0x01: If you call assert with an argument that evaluates to false.
      • 0x11: If an arithmetic operation results in underflow or overflow outside of an unchecked { ... } block.
      • 0x12: If you divide or modulo by zero (e.g. 5 / 0 or 23 % 0).
      • 0x21: If you convert a value that is too big or negative into an enum type.
      • 0x22: If you access a storage byte array that is incorrectly encoded.
      • 0x31: If you call .pop() on an empty array.
      • 0x32: If you access an array, bytesN or an array slice at an out-of-bounds or negative index (i.e. x[i] where i >= x.length or i < 0).
      • 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.

Summary

  • assert: For checking invariants and internal errors; triggers a Panic.
  • require: For input validation and condition checks; reverts on failure with a custom error message.
  • revert: For explicit transaction reversion with detailed error messages.
  • try/catch: For handling exceptions from external function calls and managing errors from other contracts.
  • Error and Panic: Built-in error types for recoverable (Error) and unrecoverable (Panic) situations.

By using these error-handling mechanisms appropriately, you can make your Solidity smart contracts more robust, secure, and easier to debug.

Presentify

Take your presentation to the next level.

FaceScreen

Put your face and name on your screen.

ToDoBar

Your to-dos on your menu bar.

Ram Patra Published on August 18, 2024
Image placeholder

Keep reading

If this article was helpful, others might be too

question solidity blockchain August 16, 2024 What is the difference between internal and private functions, and when should you use each?

In Solidity, both internal and private functions are restricted to the contract they are defined in, but they differ in terms of inheritance and accessibility. Understanding these differences is important for implementing the right access control and ensuring proper encapsulation within your smart contracts.

question solidity ethereum August 18, 2024 Different Data Types in Solidity and their default values

Solidity provides various data types, each with a specific purpose and behavior. When a variable is declared in Solidity but not explicitly initialized, it is assigned a default value depending on its type. Below is a comprehensive list of the different data types in Solidity along with their default values.

question solidity blockchain August 16, 2024 Inheritance and Overriding in Solidity

In Solidity, overriding allows a derived (child) contract to modify or extend the behavior of functions defined in a base (parent) contract. This is a key feature in object-oriented programming and enables the implementation of polymorphism, where a child contract can provide a specific implementation of a function defined in the parent contract.