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 anassert
condition fails, it indicates a bug in the contract. - Behavior: When an
assert
fails, it triggers aPanic
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 functionf
is executed even if condition istrue
.
3. revert
Statement
- Purpose:
revert
is used to explicitly revert the transaction and provide an error message. It’s commonly used withinif
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 usetry
. - Behavior: Executes the
try
block, and if an error occurs, the control flow is transferred to thecatch
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 arequire
,revert
, orinvalid
opcode is encountered. It indicates a recoverable error.- Use Cases: Failed
require
checks, user-defined errors.
- Use Cases: Failed
Panic(uint256)
: Thrown byassert
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 callassert
with an argument that evaluates tofalse
.0x11
: If an arithmetic operation results in underflow or overflow outside of anunchecked { ... }
block.0x12
: If you divide or modulo by zero (e.g.5 / 0
or23 % 0
).0x21
: If you convert a value that is too big or negative into anenum
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 aPanic
.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
andPanic
: 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.