Custom errors for commonly-used tokens
Abstract
This EIP defines a standard set of custom errors for commonly-used tokens, which are defined as ERC-20, ERC-721, and ERC-1155 tokens.
Ethereum applications and wallets have historically relied on revert reason strings to display the cause of transaction errors to users. Recent Solidity versions offer rich revert reasons with error-specific decoding (sometimes called “custom errors”). This EIP defines a standard set of errors designed to give at least the same relevant information as revert reason strings, but in a structured and expected way that clients can implement decoding for.
Motivation
Since the introduction of Solidity custom errors in v0.8.4, these have provided a way to show failures in a more expressive and gas efficient manner with dynamic arguments, while reducing deployment costs.
However, ERC-20, ERC-721, ERC-1155 were already finalized when custom errors were released, so no errors are included in their specification.
Standardized errors allow users to expect more consistent error messages across applications or testing environments, while exposing pertinent arguments and overall reducing the need of writing expensive revert strings in the deployment bytecode.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
The following errors were designed according to the criteria described in Rationale.
This EIP defines standard errors that may be used by implementations in certain scenarios but it does not specify whether implementations should revert in those scenarios, which remains up to the implementers unless a revert is mandated by the corresponding EIPs.
The names of the error arguments are defined in the Parameter Glossary and MUST be used according to those definitions.
ERC-20
ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)
Indicates an error related to the current balance
of a sender
.
Used in transfers.
Usage guidelines:
balance
MUST be less thanneeded
.
ERC20InvalidSender(address sender)
Indicates a failure with the token sender
.
Used in transfers.
Usage guidelines:
- RECOMMENDED for disallowed transfers from the zero address.
- MUST NOT be used for approval operations.
- MUST NOT be used for balance or allowance requirements.
- Use
ERC20InsufficientBalance
orERC20InsufficientAllowance
instead.
- Use
ERC20InvalidReceiver(address receiver)
Indicates a failure with the token receiver
.
Used in transfers.
Usage guidelines:
- RECOMMENDED for disallowed transfers to the zero address.
- RECOMMENDED for disallowed transfers to non-compatible addresses (eg. contract addresses).
- MUST NOT be used for approval operations.
ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed)
Indicates a failure with the spender
’s allowance
.
Used in transfers.
Usage guidelines:
allowance
MUST be less thanneeded
.
ERC20InvalidApprover(address approver)
Indicates a failure with the approver
of a token to be approved.
Used in approvals.
Usage guidelines:
- RECOMMENDED for disallowed approvals from the zero address.
- MUST NOT be used for transfer operations.
ERC20InvalidSpender(address spender)
Indicates a failure with the spender
to be approved.
Used in approvals.
Usage guidelines:
- RECOMMENDED for disallowed approvals to the zero address.
- RECOMMENDED for disallowed approvals to the owner itself.
- MUST NOT be used for transfer operations.
- Use
ERC20InsufficientAllowance
instead.
- Use
ERC-721
ERC721InvalidOwner(address owner)
Indicates that an address can’t be an owner. Used in balance queries.
Usage guidelines:
- RECOMMENDED for addresses whose ownership is disallowed (eg. ERC-721 explicitly disallows
address(0)
to be an owner). - MUST NOT be used for transfers.
- Use
ERC721IncorrectOwner
instead.
- Use
ERC721NonexistentToken(uint256 tokenId)
Indicates a tokenId
whose owner
is the zero address.
Usage guidelines:
- The
tokenId
MUST BE a non-minted or burned token.
ERC721IncorrectOwner(address sender, uint256 tokenId, address owner)
Indicates an error related to the ownership over a particular token. Used in transfers.
Usage guidelines:
sender
MUST NOT beowner
.- MUST NOT be used for approval operations.
ERC721InvalidSender(address sender)
Indicates a failure with the token sender
.
Used in transfers.
Usage guidelines:
- RECOMMENDED for disallowed transfers from the zero address.
- MUST NOT be used for approval operations.
- MUST NOT be used for ownership or approval requirements.
- Use
ERC721IncorrectOwner
orERC721InsufficientApproval
instead.
- Use
ERC721InvalidReceiver(address receiver)
Indicates a failure with the token receiver
.
Used in transfers.
Usage guidelines:
- RECOMMENDED for disallowed transfers to the zero address.
- RECOMMENDED for disallowed transfers to non-
ERC721TokenReceiver
contracts or those that reject a transfer. (eg. returning an invalid response inonERC721Received
). - MUST NOT be used for approval operations.
ERC721InsufficientApproval(address operator, uint256 tokenId)
Indicates a failure with the operator
’s approval.
Used in transfers.
Usage guidelines:
isApprovedForAll(owner, operator)
MUST be false for thetokenId
’s owner andoperator
.getApproved(tokenId)
MUST not beoperator
.
ERC721InvalidApprover(address approver)
Indicates a failure with the owner
of a token to be approved.
Used in approvals.
Usage guidelines:
- RECOMMENDED for disallowed approvals from the zero address.
- MUST NOT be used for transfer operations.
ERC721InvalidOperator(address operator)
Indicates a failure with the operator
to be approved.
Used in approvals.
Usage guidelines:
- RECOMMENDED for disallowed approvals to the zero address.
- The
operator
MUST NOT be the owner of the approved token. - MUST NOT be used for transfer operations.
- Use
ERC721InsufficientApproval
instead.
- Use
ERC-1155
ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId)
Indicates an error related to the current balance
of a sender
.
Used in transfers.
Usage guidelines:
balance
MUST be less thanneeded
for atokenId
.
ERC1155InvalidSender(address sender)
Indicates a failure with the token sender
.
Used in transfers.
Usage guidelines:
- RECOMMENDED for disallowed transfers from the zero address.
- MUST NOT be used for approval operations.
- MUST NOT be used for balance or allowance requirements.
- Use
ERC1155InsufficientBalance
orERC1155MissingApprovalForAll
instead.
- Use
ERC1155InvalidReceiver(address receiver)
Indicates a failure with the token receiver
.
Used in transfers.
Usage guidelines:
- RECOMMENDED for disallowed transfers to the zero address.
- RECOMMENDED for disallowed transfers to non-
ERC1155TokenReceiver
contracts or those that reject a transfer. (eg. returning an invalid response inonERC1155Received
). - MUST NOT be used for approval operations.
ERC1155MissingApprovalForAll(address operator, address owner)
Indicates a failure with the operator
’s approval in a transfer.
Used in transfers.
Usage guidelines:
isApprovedForAll(owner, operator)
MUST be false for thetokenId
’s owner andoperator
.
ERC1155InvalidApprover(address approver)
Indicates a failure with the approver
of a token to be approved.
Used in approvals.
Usage guidelines:
- RECOMMENDED for disallowed approvals from the zero address.
- MUST NOT be used for transfer operations.
ERC1155InvalidOperator(address operator)
Indicates a failure with the operator
to be approved.
Used in approvals.
Usage guidelines:
- RECOMMENDED for disallowed approvals to the zero address.
- MUST be used for disallowed approvals to the owner itself.
- MUST NOT be used for transfer operations.
- Use
ERC1155InsufficientApproval
instead.
- Use
ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength)
Indicates an array length mismatch between ids
and values
in a safeBatchTransferFrom
operation.
Used in batch transfers.
Usage guidelines:
idsLength
MUST NOT bevaluesLength
.
Parameter Glossary
Name | Description |
---|---|
sender |
Address whose tokens are being transferred. |
balance |
Current balance for the interacting account. |
needed |
Minimum amount required to perform an action. |
receiver |
Address to which tokens are being transferred. |
spender |
Address that may be allowed to operate on tokens without being their owner. |
allowance |
Amount of tokens a spender is allowed to operate with. |
approver |
Address initiating an approval operation. |
tokenId |
Identifier number of a token. |
owner |
Address of the current owner of a token. |
operator |
Same as spender . |
*Length |
Array length for the prefixed parameter. |
Error additions
Any addition to this EIP or implementation-specific errors (such as extensions) SHOULD follow the guidelines presented in the rationale section to keep consistency.
Rationale
The chosen objectives for a standard for token errors are to provide context about the error, and to make moderate use of meaningful arguments (to maintain the code size benefits with respect to strings).
Considering this, the error names are designed following a basic grammatical structure based on the standard actions that can be performed on each token and the subjects involved.
Actions and subjects
An error is defined based on the following actions that can be performed on a token and its involved subjects:
- Transfer: An operation in which a sender moves to a receiver any number of tokens (fungible balance and/or non-fungible token ids).
- Approval: An operation in which an approver grants any form of approval to an operator.
These attempt to exhaustively represent what can go wrong in a token operation. Therefore, the errors can be constructed by specifying which subject failed during an action execution, and prefixing with an error prefix.
Note that the action is never seen as the subject of an error.
If a subject is called different on a particular token standard, the error should be consistent with the standard’s naming convention.
Error prefixes
An error prefix is added to a subject to derive a concrete error condition. Developers can think about an error prefix as the why an error happened.
A prefix can be Invalid
for general incorrectness, or more specific like Insufficient
for amounts.
Domain
Each error’s arguments may vary depending on the token domain. If there are errors with the same name and different arguments, the Solidity compiler currently fails with a DeclarationError
.
An example of this is:
InsufficientApproval(address spender, uint256 allowance, uint256 needed);
InsufficientApproval(address operator, uint256 tokenId);
For that reason, a domain prefix is proposed to avoid declaration clashing, which is the name of the ERC and its corresponding number appended at the beginning.
Example:
ERC20InsufficientApproval(address spender, uint256 allowance, uint256 needed);
ERC721InsufficientApproval(address operator, uint256 tokenId);
Arguments
The selection of arguments depends on the subject involved, and it should follow the order presented below:
- Who is involved with the error (eg.
address sender
) - What failed (eg.
uint256 allowance
) - Why it failed, expressed in additional arguments (eg.
uint256 needed
)
A particular argument may fall into overlapping categories (eg. Who may also be What), so not all of these will be present but the order shouldn’t be broken.
Some tokens may need a tokenId
. This is suggested to include at the end as additional information instead of as a subject.
Error grammar rules
Given the above, we can summarize the construction of error names with a grammar that errors will follow:
<Domain><ErrorPrefix><Subject>(<Arguments>);
Where:
- Domain:
ERC20
,ERC721
orERC1155
. Although other token standards may be suggested if not considered in this EIP. - ErrorPrefix:
Invalid
,Insufficient
, or another if it’s more appropriate. - Subject:
Sender
,Receiver
,Balance
,Approver
,Operator
,Approval
or another if it’s more appropriate, and must make adjustments based on the domain’s naming convention. - Arguments: Follow the who, what and why order.
Backwards Compatibility
Tokens already deployed rely mostly on revert strings and make use of require
instead of custom errors. Even most of the newly deployed tokens since Solidity’s v0.8.4 release inherit from implementations using revert strings.
This EIP can not be enforced on non-upgradeable already deployed tokens, however, these tokens generally use similar conventions with small variations such as:
- including/removing the domain.
- using different error prefixes.
- including similar subjects.
- changing the grammar order.
Upgradeable contracts MAY be upgraded to implement this EIP.
Implementers and DApp developers that implement special support for tokens that are compliant with this EIP, SHOULD tolerate different errors emitted by non-compliant contracts, as well as classic revert strings.
Reference Implementation
Solidity
pragma solidity ^0.8.4;
/// @title Standard ERC20 Errors
/// @dev See https://eips.ethereum.org/EIPS/eip-20
/// https://eips.ethereum.org/EIPS/eip-6093
interface ERC20Errors {
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
error ERC20InvalidSender(address sender);
error ERC20InvalidReceiver(address receiver);
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
error ERC20InvalidApprover(address approver);
error ERC20InvalidSpender(address spender);
}
/// @title Standard ERC721 Errors
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// https://eips.ethereum.org/EIPS/eip-6093
interface ERC721Errors {
error ERC721InvalidOwner(address owner);
error ERC721NonexistentToken(uint256 tokenId);
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
error ERC721InvalidSender(address sender);
error ERC721InvalidReceiver(address receiver);
error ERC721InsufficientApproval(address operator, uint256 tokenId);
error ERC721InvalidApprover(address approver);
error ERC721InvalidOperator(address operator);
}
/// @title Standard ERC1155 Errors
/// @dev See https://eips.ethereum.org/EIPS/eip-1155
/// https://eips.ethereum.org/EIPS/eip-6093
interface ERC1155Errors {
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
error ERC1155InvalidSender(address sender);
error ERC1155InvalidReceiver(address receiver);
error ERC1155MissingApprovalForAll(address operator, address owner)
error ERC1155InvalidApprover(address approver);
error ERC1155InvalidOperator(address operator);
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
Security Considerations
There are no known signature hash collisions for the specified errors.
Tokens upgraded to implement this EIP may break assumptions in other systems relying on revert strings.
Offchain applications should be cautious when dealing with untrusted contracts that may revert using these custom errors. For instance, if a user interface prompts actions based on error decoding, malicious contracts could exploit this to encourage untrusted and potentially harmful operations.
Copyright
Copyright and related rights waived via CC0.