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 17, 2024 Deleting an element from an array in Solidity

In Solidity, deleting an element from an array involves several considerations because Solidity arrays are either of fixed size or dynamic size, and their elements are stored in different data locations (storage, memory, calldata). Here’s how you can delete elements from arrays in different contexts:

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.

question solidity blockchain August 16, 2024 Data Types in Solidity and how to initialise each

In Solidity, variables can be categorized based on their data types, and each type has specific ways to initialize them. Here’s an overview of different variable types in Solidity and how to initialize them: