5

This applies only to solidity ^0.8 and onward.

Let's say I have a function like this:

function withdraw() external {
  (bool success, ) = payable(msg.sender).call{value: address(this).balance}("");
  // Now we should check the function succeeded
}

I could check this with:

require(success, "Transfer failed");

or

// At the top of the code
error TransferFailed();
.
.
.
if(!success){ revert TransferFailed();}

Where does it make sense to use require vs a custom error and revert? It seems you'll just need to check the gas costs, and that's it.

3
  • Custom errors make sense when the caller might want to do something with them and detect what kind of an error was raised. Commented Mar 8, 2022 at 19:45
  • Sounds like it might be sort of "whatever the dev feels like doing?" Commented Mar 9, 2022 at 0:59
  • Better to raise in every case? Is there any downside? Commented Mar 9, 2022 at 8:46

3 Answers 3

6

Custom errors are more gas efficient than using require with a string explanation. So ideally you'd always use this over require.

2
  • what do you mean by gas efficient? whether you use require, revert, custom error, they're all refund gas back in case of failure, isn't it? in our applications, if contract supposed to fail, client libraires simply throw error right(not even send transaction to blockchain so gas will not even be consumed.)? why gas efficient? you mean deploy cost? or am I missing anything?
    – ajay
    Commented Oct 22, 2022 at 6:04
  • 1
    The reverting process costs gas to do, so when the function reverts, its more gas efficient to revert with a custom error. Check the solidity blog: blog.soliditylang.org/2021/04/21/custom-errors Commented Oct 25, 2022 at 12:47
3

When reviewing the Impostors NFT contract (which exclusively uses custom errors with revert), they noted this in regards to using custom errors:

  It saves bytecode to revert on custom errors instead of using require
  statements.

Haven't tested to see if this is the case, but based on the above statement, it would seem custom errors save bytecode in comparison to using require. This technically makes sense if you are reusing the custom errors, as you are not writing the string message argument from require multiple times.

1
  • sometimes a string can use much less bytes than a struct, because struct is abi-encoded to 32 byte fields. 2 fields - 64 bytes . A string of 20-30 characters would be more gas-friendly
    – Nulik
    Commented Jun 24 at 22:36
1

Custom errors reduce the cost to deploy and call a function on a contract. Errors also provide parameterization which gives the error much more context of the reasons why the error occurred. Compare this with require which you can only return string messages back. Furthermore, deployment and transaction invocation costs more gas as shown below.

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.10;

error Unauthorized(address caller);

contract CardMachine {
    address public owner;
    address payable public cardMachine;

    constructor() payable {
        owner = msg.sender;
        cardMachine = payable(address(this));
    }

    function withdraw(uint256 _amount) public {
        if (msg.sender != owner)
            revert Unauthorized(msg.sender);

        cardMachine.call{ value: _amount }("");
    }
}

This costs 292,874 gas to deploy in Remix with sending 1 ether, and calling withdraw(1000000000000000000) with an unauthorized address costs 24,051 gas.

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.10;

contract CardMachine {
    address public owner;
    address payable public cardMachine;

    constructor() payable {
        owner = msg.sender;
        cardMachine = payable(address(this));
    }

    function withdrawWithRequire(uint256 _amount) public {
        require(msg.sender == owner, "Caller is unauthorized to withdraw");
            
        cardMachine.call{ value: _amount }("");
    }
}

This costs 327,273 to deploy in Remix with sending 1 ether, and calling withdraw(1000000000000000000) with an unauthorized address costs 24,120 gas. You also do not know who the caller was unless you traced the transaction hash to see the original caller.

Custom errors are ABI encoded, and can be decoded using existing ABI decoders. This makes it a lot more efficient to store and use compared to strings.

A detailed explanation: https://blog.soliditylang.org/2021/04/21/custom-errors/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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