try-catch-in-solidity-banner

Solidity’s try-catch Syntax: Best Practices for Error Handling with Examples

Introduction

When making smart contracts on the Ethereum blockchain, handling errors can be a very important part of the process. Especially when there aren’t many tools available! Solidity v0.6.0’s addition of the try/catch syntax was a big step in the right direction for how errors are handled. These new features build on previous ones, like v0.4.22, which added reason strings to the revert and require statements.

You might be wondering why error-handling abilities are so important in solidity-based contract development. In many cases, smart contracts represent real-world assets and have a high monetary value. Solidity’s “all-or-nothing” approach to transactions hasn’t always made it possible to handle errors in a nuanced way. Imagine putting a lot of money into a smart contract, only to have the transaction fail at the last minute, leaving you to wonder why there wasn’t more detailed error handling.

With the try/catch syntax added in v0.6.0, developers now have the tools they need to handle complex external call failures without having to roll back an entire transaction. While state changes in the called function are still rolled back, error handling on a per-transaction basis offers better practical behavior alignment.

We will be exploring the Solidity try/catch syntax in detail in this post, with examples.

Using ‘try catch’ in Smart Contracts

The try/catch statement lets you find and respond to failed external calls and failed calls to create a contract. This gives you more control over how errors are handled in Solidity contracts. But it is important to know that the try/catch statement can’t be used for internal function calls.

Take a look at this simple example to see how to use the try/catch statement. We have two contracts: HelloWorld and HelloWorldFactory.

The HelloWorld contract has a very simple function that returns a message string:

The HelloWorldFactory contract creates and manages HelloWorld instances. The createHelloWorld function creates a new instance of HelloWorld and stores it in the contracts mapping. If the transaction fails, the try/catch block detects the error and adds it to the variable errorCount.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract HelloWorld {
function showMessage() public pure returns (string memory) {
return “Hello, World!”;
}
}
contract HelloWorldFactory {
mapping (address => HelloWorld) public contracts;
uint public errorCount;
function createHelloWorld() public {
try new HelloWorld()
returns (HelloWorld newContract)
{
contracts[msg.sender] = newContract;
} catch {
errorCount++;
}
}
}

Using try/catch syntax in this way makes it easier to align behavior in transaction lifecycles and gives Solidity contracts more precise control over how to handle errors.

Retrieving the Error Message

When you use the try/catch syntax, you can get more information about why a transaction failed. Solidity offers two ways to get error messages.

In the last example, we created two contracts: HelloWorld and HelloWorldFactory. The HelloWorld contract had a function that returned a message string, and the HelloWorldFactory contract created and managed HelloWorld instances.

To illustrate the two methods of retrieving error messages, we will modify the createHelloWorld function in the HelloWorldFactory contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract HelloWorld {
function showMessage() public pure returns (string memory) {
return “Hello, World!”;
}
}
contract HelloWorldFactory {
mapping (address => HelloWorld) public contracts;
string public errorMessage;
bytes public errorData;
function createHelloWorld() public {
try new HelloWorld()
returns (HelloWorld newContract)
{
contracts[msg.sender] = newContract;
} catch Error(string memory reason) {
errorMessage = reason;
} catch (bytes memory data) {
errorData = data;
}
}
}

In this example, we have modified the createHelloWorld function to include two catch statements. The first catch statement, catch Error(string memory reason), catches the error and passes the error message with the reason variable, which we store in the errorMessage string.

The second catch statement, catch (bytes memory data), retrieves a byte memory buffer if an error occurs. In this case, we store the byte memory buffer in the errorData bytes variable.

Using error messages in Solidity contracts can be an effective debugging tool for developers, as it provides finer control over error handling in Solidity contracts.

solidity try catch meme

Advantages and disadvantages of each method

Advantages of catch Error(string memory reason)

One advantage of using the catch Error(string memory reason) statement is that it provides a human-readable error message, which is easier to interpret and debug for developers. The string variable also allows for easier filtering and categorization of errors.

Disadvantages of catch Error(string memory reason)

The main disadvantage of using catch Error(string memory reason) is that it can be more expensive in terms of gas usage. This is because, in Solidity, string variables are stored after the contract code, which requires more gas.

Advantages of catch (bytes memory reason)

One advantage of using the catch (bytes memory reason) statement is that it retrieves a byte memory buffer that can contain arbitrary data, including multiple data types. This can be useful for debugging more complex and diverse errors.

Another advantage is that using bytes variables is cheaper in terms of gas usage. This is because bytes variables are stored in the first 32 bytes of memory, which requires less gas.

Disadvantages of catch (bytes memory reason)

The main disadvantage of using catch (bytes memory reason) is that the error message is not human-readable, making it more difficult for developers to debug and understand the error. Additionally, because the bytes variable can contain arbitrary data, it can be more difficult to filter and categorize the errors.

Conclusion

The try/catch syntax in Solidity is an important tool for handling errors in smart contract development. By using the try/catch statement, developers can catch errors in external calls, and retrieve error messages using either the catch Error(string memory reason) or catch (bytes memory reason) statement.

Each method has its own advantages and disadvantages, depending on the context of the error handling. The catch Error(string memory reason) statement provides human-readable error messages, while the catch (bytes memory reason) statement is cheaper in terms of gas usage and supports arbitrary data types.

Additionally, Solidity v0.8.0 introduced the Error type, which allows developers to define their own error types and provide more specific and detailed error messages to users. Read more about custom errors here.

Thank you for reading! If you found this post helpful, feel free to follow me on my social media channels for more updates on Solidity development and smart contract best practices.

PS: Also, I recently wrote a new book, “Proof-of-Stake For Beginners: A guide to understanding the inner workings of Ethereum’s Proof-of-Stake protocol” which you can access at this link. It covers everything you need to know about Ethereum’s transition from proof-of-work to proof-of-stake, including basic concepts, staking, rewards, and more. I hope it can help you understand the revolutionary changes that are coming to the Ethereum ecosystem!

Again, thank you for your interest in Solidity development, and I look forward to sharing more with you in the future.

%d bloggers like this: