5

Does Solidity support try-catch functionality? If so, how does it work? What are the limitations?

4

Solidity 0.6.0 and Greater (Updated Apr 2020)

As of Solidity 0.6.0, there is try-catch functionality built into the language. The syntax is similar to existing languages, however functionality is limited at this time to try expressions representing an external function call or a contract creation.

The try part of the try-catch functionality is only able to the aforementioned expressions. A reversion within a try block will not be caught. This means that that the following code will cause a reverted transaction (see below for a full example):

try feed.getData(token) returns (uint v) {
    require(1 == 2, "Will revert");  // This will cause the transaction to revert!!
} catch (bytes memory /*lowLevelData*/) {
    return (0);  // Will not get here
}

Currently, Solidity supports two types of catch blocks:

  1. catch Error (string memory /*reason*/)

    • If the error was caused by revert("reasonString") or require(false, "reasonString")(or an internal error that causes such an exception), then the catch clause of the type catch Error(string memory reason) will be executed.
  2. catch (bytes memory /*lowLevelData*/)

    • This catch block handles all other cases. This block executed if the error signature does not match any other clause, there was an error during decoding of the error message, if there was a failing assertion in the external call (for example due to a division by zero or a failing assert()) or if no error data was provided with the exception.

Solidity plans to support other types of error data in the future.

If you are not interested in the error data, you can just use catch { ... } (even as the only catch clause).

Note: this article dives even deeper than my post here.

Examples

Simplified Example

pragma solidity ^0.6.0;

    interface AddNumbers { function add(uint256 a, uint256 b) external returns (uint256 c); }

contract Example {
    AddNumbers addContract;
    event StringFailure(string stringFailure);
    event BytesFailure(bytes bytesFailure);

    function exampleFunction(uint256 _a, uint256 _b) public returns (uint256 _c) {

        try addContract.add(_a, _b) returns (uint256 _value) {
            return (_value);
        } catch Error(string memory _err) {
            // This may occur if there is an overflow with the two numbers and the `AddNumbers` contract explicitly fails with a `revert()`
            emit StringFailure(_err);
        } catch (bytes memory _err) {
            emit BytesFailure(_err):
        }
    }
}

Full Example (From Solidity Docs)

pragma solidity ^0.6.0;

interface DataFeed { function getData(address token) external returns (uint value); }

contract FeedConsumer {
    DataFeed feed;
    uint errorCount;
    function rate(address token) public returns (uint value, bool success) {
        // Permanently disable the mechanism if there are
        // more than 10 errors.
        require(errorCount < 10);
        try feed.getData(token) returns (uint v) {
            return (v, true);
        } catch Error(string memory /*reason*/) {
            // This is executed in case
            // revert was called inside getData
            // and a reason string was provided.
            errorCount++;
            return (0, false);
        } catch (bytes memory /*lowLevelData*/) {
            // This is executed in case revert() was used
            // or there was a failing assertion, division
            // by zero, etc. inside getData.
            errorCount++;
            return (0, false);
        }
    }
}


Solidity < 0.6.0

For Solidity versions prior to 0.6.0, there was no try-catch functionality built into the language. There are, however, ways to perform similar functionality given the available language features at the time.

By following this tutorial by Mudit Gupta of Polymath, you can closely mimic try-catch functionality without the try and catch keywords:

pragma solidity ^0.5.0;

import "./Token.sol";

/**
 * @dev This contract showcases a simple Try-catch call in Solidity
 */
contract Example {
    Token public token;
    uint256 public lastAmount;

    constructor(Token _token) public {
        token = _token;
    }

    event TransferFromFailed(uint256 _amount);

    function tryTransferFrom(address _from, address _to, uint256 _amount) public returns(bool returnedBool, uint256 returnedAmount) {
        lastAmount = _amount; // We can query this after transferFrom reverts to confirm that the whole transaction did NOT revert
        // and the changes we made to the state are still present.

        (bool success, bytes memory returnData) =
            address(token).call( // This creates a low level call to the token
                abi.encodePacked( // This encodes the function to call and the parameters to pass to that function
                    token.transferFrom.selector, // This is the function identifier of the function we want to call
                    abi.encode(_from, _to, _amount) // This encodes the parameter we want to pass to the function
                )
            );
        if (success) { // transferFrom completed successfully (did not revert)
            (returnedBool, returnedAmount) = abi.decode(returnData, (bool, uint256));
        } else { // transferFrom reverted. However, the complete tx did not revert and we can handle the case here.
            // I will emit an event here to show this
            emit TransferFromFailed(_amount);
        }
    }
}
| improve this answer | |
  • Thanks for posting this. However I find this confusing as they seem to conflict ` A reversion within a try block will not be caught. ` and If the error was caused by revert("reasonString") or require(false, "reasonString")(or an internal error that causes such an exception), then the catch clause of the type catch Error(string memory reason) will be executed. – Samuel Dare Dec 30 '19 at 12:35
  • I think this is what it meant Errors inside the expression are not caught (for example if it is a complex expression that also involves internal function calls), only a revert happening inside the external call itself. . Maybe error handling for delegate/ staticcalls cannot be handled within the same try block – Samuel Dare Dec 30 '19 at 13:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.