DEV Community

Block Experts
Block Experts

Posted on

A Walkthrough of Solidity Custom Errors

Image descriptionAs blockchain smart contracts developer, efficiency and clarity in smart contract development are always my top priority. Solidity, the most popular programming language for Ethereum smart contracts, introduced custom errors in version 0.8.4 via revert function and make it available in solidity 0.8.26 via require and could be used only via via-ir pipeline. This feature allows to optimize gas and improve the readability of error messages. This article provides a comprehensive walkthrough of Solidity custom errors, their advantages, why it must be used, and implementation.


Why Use Custom Errors?

Traditional methods of error handling in Solidity primarily rely on require, revert, and assert statements with strings as error messages. While effective, these methods have limitations:

  1. High Gas Costs: Storing long error strings consumes more gas.
  2. Lack of Structure: Error strings provide limited context and can be challenging to parse programmatically.

Custom errors address these issues by:

  • Reducing gas consumption, as error data is encoded compactly.
  • Allowing developers to define structured errors with parameters, making debugging easier.

Gas Optimization Example

To understand the gas savings, consider the following comparison:

Using require with an Error Message

require(condition, "Insufficient balance for withdrawal");
Enter fullscreen mode Exit fullscreen mode

Using a Custom Error

error InsufficientBalance(address account, uint requested, uint available);

require(condition, InsufficientBalance(msg.sender, requestedAmount, availableBalance));
Enter fullscreen mode Exit fullscreen mode

Gas Cost Analysis

  • require with string: The gas cost increases with the length of the string. For example, a 32-character error message can add 200-300 gas.
  • Custom error: Encodes error data more compactly, reducing gas costs significantly. Using a custom error in this scenario can save ~100-150 gas per revert.

While savings may seem small per transaction, they add up significantly in high-frequency or large-scale dApps.


Syntax and Structure of Custom Errors

Custom errors are defined at the contract level using the error keyword. Here's the basic syntax:

error CustomErrorName(string parameter1, uint parameter2);
Enter fullscreen mode Exit fullscreen mode

You can then use require with the custom error to abort execution and provide specific error details:

require(condition, CustomErrorName("Reason", 42))
Enter fullscreen mode Exit fullscreen mode

Practical Example: Using Custom Errors

Let’s explore a practical example to understand how custom errors work.

Contract: Simple Bank

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract SimpleBank {
    mapping(address => uint) private balances;

    error InsufficientBalance(address account, uint requested, uint available);

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint amount) external {
        uint balance = balances[msg.sender];
        require(amount <= balance, InsufficientBalance(msg.sender, amount, balance);


        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }

    function getBalance() external view returns (uint) {
        return balances[msg.sender];
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. Defining the Error:
    The InsufficientBalance error includes the account address, the requested withdrawal amount, and the available balance.

  2. Using the Error:
    In the withdraw function, the contract checks if the requested amount exceeds the balance. If so, it uses revert with InsufficientBalance to provide detailed error data.

  3. Gas Savings:
    Unlike traditional error strings, custom errors reduce the gas cost by encoding error data in a compact form.


Interpreting Custom Errors in Transactions

When a transaction fails due to a custom error, the error details are accessible through the transaction’s revert reason. Tools like Etherscan, Hardhat, and Foundry can decode these errors, enabling developers to debug efficiently.

Enable via-ir in hardhat

here is how to enable via-ir pipeline:

const config: HardhatUserConfig = {
  ...
  defaultNetwork: "sepolia",
  solidity: {
    version: "0.8.26", // any version you want
    settings: {
      viaIR: true,
  },

};
Enter fullscreen mode Exit fullscreen mode

Decoding Example:

Using Hardhat, you can decode custom errors as follows:

  1. Run the failing test or transaction.
  2. Use the debug output to inspect the revert reason.
Error: VM Exception while processing transaction: reverted with custom error 'InsufficientBalance'("0xYourAddress", 100, 50)
Enter fullscreen mode Exit fullscreen mode

Best Practices for Custom Errors

  1. Be Descriptive: Define custom errors with meaningful names and parameters.
  2. Group Errors: If a contract has many errors, consider grouping them using comments or organizing them by functionality.
  3. Combine with Events: Use custom errors alongside events for comprehensive debugging and logging.

Conclusion

Custom errors in Solidity are a powerful tool for writing efficient and maintainable smart contracts. By reducing gas costs and improving error transparency, they enable to build optimized and readable smart contracts. Integrate custom errors into your projects today and elevate your smart contract development game!
Here a real example smart contracts that uses custom errors

🌟 Useful Tools for Blockchain Developers


Top comments (0)