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 17, 2024 Understanding call, delegatecall, and staticcall in Solidity with real-world analogy

Let’s break down call, delegatecall, and staticcall in Solidity using simple analogies and real-world examples.

question ethereum solidity August 17, 2024 What are the different ways to send ether to a smart contract in Ethereum?

In Solidity, there are several ways to send Ether to a smart contract. Each method serves different use cases and offers varying levels of control and flexibility. Here’s a summary of the different approaches: