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/