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 than needed.

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 or ERC20InsufficientAllowance instead.

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 than needed.

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.

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.

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 be owner.
  • 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 or ERC721InsufficientApproval instead.

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 in onERC721Received).
  • 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 the tokenId’s owner and operator.
  • getApproved(tokenId) MUST not be operator.

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.

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 than needed for a tokenId.

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 or ERC1155MissingApprovalForAll instead.

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 in onERC1155Received).
  • 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 the tokenId’s owner and operator.

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.

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 be valuesLength.

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:

  1. Who is involved with the error (eg. address sender)
  2. What failed (eg. uint256 allowance)
  3. 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 or ERC1155. 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:

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 and related rights waived via CC0.