Error Handling in Solidity

In this article, we will discuss the different types of errors that can occur in Solidity and explore best practices for error handling to avoid common pitfalls and vulnerabilities.



Solidity Error Handling

Solidity has many functions for handling errors.

An error can occur at compile time or during runtime.

A syntax error check is performed at compile-time in Solidity, but runtime errors are difficult to catch and occur primarily during contract execution.

Out-of-gas errors, overflowing data types, divide by zero errors, array-out-of-index errors, etc., are some of the runtime errors.

Solidity had a single throw statement until version 4.10. To handle errors, multiple if…else statements must be implemented to check the values and throw errors.

New error handling constructs assert, require, and revert were introduced after version 4.10.


Require Statements

A require statement specifies the constraints that must be met before the code can be executed.

The function takes only one argument and upon completion of the evaluation process, it returns a boolean value.

In addition to that, it has the option to output custom string messages as well. There is an exception raised if the condition is false and the execution is terminated as a result.

Upon reversal of the state, the unused gas is returned back to the caller, and the state is reverted to the original state.

Here are some examples of situations in which a required type of exception is triggered:

  • In the case where require() is called with arguments that result in false results.
  • In this case, a function that is called by a message does not end properly when the message is called.
  • It is possible that when new keywords are used to create the contract, the process does not end properly.
  • When a codeless contract relates to an external function.
  • Using the public getter method to send ethers to the contract.
  • There is a problem with the .transfer() method.

The given example shows the working of the require statements in an easier way. The function below the function print_Even_Only only prints or executes when an even number is assigned to it.

Example: 

pragma solidity ^0.8.0;contract SolidityTest{ uint public mrx;function print_Even_Only(uint ample) public returns(uint){ require(ample % 2 == 0, "Number must be even in nature"); mrx = ample; return ample; } }
<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>
Correspondingly, the given function gets product of mrx and ample only when both are odd in nature:

Example: 

pragma solidity ^0.8.0;contract SolidityTest{ function multiply_Odd_only(uint mrx,uint ample) public pure returns(uint){ require(mrx % 2 == 1, "Mrx must be odd in nature"); require(ample % 2 == 1, "Ample must be odd in nature");uint product=mrx*ample; return product; } }
<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>

It is possible to return the boolean value as well as a string message using the require statement as shown below:

Example: 

pragma solidity ^0.8.0;contract SolidityTest { function sum_Mrx_Ample(uint mrx,uint ample) public pure returns(uint){ require(mrx % 2 !=0, "Mrx must be an even number is nature"); require(ample % 2 ==0, "Ample must be an odd number in nature"); return mrx+ample; } }
<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>
Note: To obtain an output the mrx should be even and ample should be an odd number to get the sum by using the sum_Mrx_Ample function.

Assert Statement

Syntax-wise, it is similar to the “require” statement. Upon evaluating the condition, it returns a boolean value.

Depending on the return value, the program will either continue or throw an exception.

As a result of the assert statement, the entire gas supply is consumed, leading to the state being reversed back to its original state instead of returning unused gas.

Prior to contract execution, assert checks the current state and function conditions.

A few examples of assert types of exceptions are listed below:

  • In this case, the assert is called with a false condition.
  • A function’s zero-initialized variable is called.
  • Converting a large or negative value to an enumeration (enum).
  • A value is divided or modulo by zero.
  • When an array is accessed with an index that is too large or negative.

The given example will explain the basic working of the solidity assert statement.

Here the user input is matched with the ample_number only then it adds 50 to the user input else generates an error:

Example: 

pragma solidity ^0.8.0;contract SolidityTest{uint public result; uint public ample_number = 10;function compare_value(uint mrx_ample) public returns (uint){ assert(mrx_ample == ample_number); result = mrx_ample; return result+50; } }
<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>

The assert statement is used in the example below to check if the user entered value is greater than mrx, if it finds it true then it prints a message else shows an error:

Example: 

pragma solidity ^0.8.0;contract SolidityTest{uint public mrx = 30; string mr_x="Ample is greater than mrx";function print_Message(uint ample) public view returns (string memory){ assert(ample > mrx);return mr_x; } }
<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>

Revert Statement

Similar to a require statement, this statement returns a value.

The function does not evaluate any condition and does not depend on any state or statement.

The function generates exceptions, displays errors, and reverts the call.

The string message in this statement indicates the problem related to the exception information.

When calling a revert statement, the state is returned to its original state, an exception is thrown, and unused gas is returned.

Revert handles the same types of exceptions as require, but with a little bit more complexity.

The given example provides the basic understanding of the revert statement. The program below takes an uint number and checks if it is zero or negative. If the result comes as true then it shows an exception, else prints the number accordingly:

Example: 

pragma solidity ^0.8.0;contract SolidityTest{ function check_Number(uint mrx) public pure returns(uint){ if (mrx == 0 || mrx < 0) { revert("Number cannot be negetive or zero"); } return mrx; } }
<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>

Correspondingly, the other examples show the working of the revert statement in a more clear way. The following example takes an integer as user input evaluates if the given integer is completely divisible by 5. If the result is true then it prints the message else an error message is generated:

Example: 

pragma solidity ^0.8.0;contract SolidityTest{string result="Greetings User !! You are learning Solidity Programming Language from Mr.Examples"; function check_Number(uint ample) public view returns(string memory){ if (ample % 5 != 0) { revert("Number is not completely divisible by 5"); } return result; } }
<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>

Solidity Error Handling Benefits

  • Using error handling statements helps prevent contract bugs. Solidity developers can avoid common security vulnerabilities like reentrancy attacks and integer overflows by verifying inputs and state conditions before executing code.
  • Explaining what went wrong when a transaction fails is crucial to improving user experience. It is possible to provide users with clear feedback about what went wrong by including informative error messages in error handling statements.
  • Reduced gas consumption: By preventing unnecessary computation when inputs are invalid or state conditions are not met, error handling statements can help reduce gas consumption. It is possible to optimize contract performance and save gas fees by reverting a transaction as soon as an error condition is detected.
  • Easier debugging: Error messages are useful when troubleshooting a transaction. Developers can identify and fix bugs in contract code more easily if they provide descriptive error messages.

Conclusion

Error handling is one of the most critical components of the development of Solidity smart contracts.

With Solidity, you can ensure their contracts are secure, efficient, and user-friendly by using error handling statements such as require, assert, and revert.

It is possible for developers to prevent bugs and unexpected behavior, improve the user experience, and reduce gas consumption by verifying inputs and state conditions before executing code, providing informative error messages, and optimizing contract performance.

The proper use of error handling statements will enable you to build smart contracts that are more reliable and secure when dealing with unexpected conditions and input errors.

We value your feedback.
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0

Subscribe To Our Newsletter
Enter your email to receive a weekly round-up of our best posts. Learn more!
icon

Leave a Reply

Your email address will not be published. Required fields are marked *