DCIPs/EIPS/eip-6093.md

371 lines
16 KiB
Markdown
Raw Normal View History

---
eip: 6093
title: Custom errors for commonly-used tokens
description: Lists custom errors for common token implementations
author: Ernesto García (@ernestognw), Francisco Giordano (@frangio), Hadrien Croubois (@Amxx)
discussions-to: https://ethereum-magicians.org/t/eip-6093-custom-errors-for-erc-tokens/12043
status: Draft
type: Standards Track
category: ERC
created: 2022-12-06
requires: 20, 721, 1155
---
## Abstract
This EIP defines a standard set of custom errors for commonly-used tokens, which are defined as [EIP-20](./eip-20.md), [EIP-721](./eip-721.md), and [EIP-1155](./eip-1155.md) 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, [EIP-20](./eip-20.md), [EIP-721](./eip-721.md), [EIP-1155](./eip-1155.md) 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](#rationale).
This EIP defines standard errors that may be used by implementations in certain scenarios, but 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](#parameter-glossary), and MUST be used according to those definitions.
### [EIP-20](./eip-20.md)
#### `ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)`
Indicates an error related to the current `balance` of a `sender`.
Used in transfers.
- MUST be used when `balance` is less than `needed`.
- MUST NOT be used if `balance` is greater than or equal to `needed`.
#### `ERC20InvalidSender(address sender)`
Indicates a failure with the token `sender`.
Used in transfers.
- MUST be used 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.
- MUST be used for disallowed transfers to the zero address.
- MUST be used 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.
- MUST be used when `allowance` is less than `needed`.
- MUST NOT be used if `allowance` is greater than or equal to `needed`.
#### `ERC20InvalidApprover(address approver)`
Indicates a failure with the `approver` of a token to be approved.
Used in approvals.
- MUST be used 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.
- MUST be used 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 `ERC20InsufficientAllowance` instead.
### [EIP-721](./eip-721.md)
#### `ERC721InvalidOwner(address sender, uint256 tokenId, address owner)`
Indicates an error related to the ownership over a particular token.
Used in transfers.
- MUST be used when `sender` is not `owner`.
- MUST NOT be used for approval operations.
#### `ERC721InvalidSender(address sender)`
Indicates a failure with the token sender.
Used in transfers.
- MUST be used for disallowed transfers from the zero address.
- MUST NOT be used for approval operations.
- MUST NOT be used for ownership or approval requirements.
- Use `ERC721InvalidOwner` or `ERC721InsufficientApproval` instead.
#### `ERC721InvalidReceiver(address receiver)`
Indicates a failure with the token receiver.
Used in transfers.
- MUST be used for disallowed transfers to the zero address.
- MUST be used 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.
- MUST be used when operator `isApprovedForAll(owner, operator)` is false.
- MUST be used when operator `getApproved(tokenId)` is not `operator`.
#### `ERC721InvalidApprover(address approver)`
Indicates a failure with the `owner` of a token to be approved.
Used in approvals.
- MUST be used 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.
- MUST be used 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 `ERC721InsufficientApproval` instead.
### [EIP-1155](./eip-1155.md)
#### `ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId)`
Indicates an error related to the current `balance` of a sender.
Used in transfers.
- MUST be used when `balance` is less than `needed` for a `tokenId`.
- MUST NOT be used if `balance` is greater than or equal to `needed` for a `tokenId`.
#### `ERC1155InvalidSender(address sender)`
Indicates a failure with the token sender.
Used in transfers.
- MUST be used 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 `ERC1155InsufficientApproval` instead.
#### `ERC1155InvalidReceiver(address receiver)`
Indicates a failure with the token receiver.
Used in transfers.
- MUST be used for disallowed transfers to the zero address.
- MUST be used 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.
#### `ERC1155InsufficientApproval(address operator, uint256 tokenId)`
Indicates a failure with the `operator`'s approval in a transfer.
Used in transfers.
- MUST be used when operator `isApprovedForAll(owner, operator, tokenId)` is false.
#### `ERC1155InvalidApprover(address approver)`
Indicates a failure with the `approver` of a token to be approved.
Used in approvals.
- MUST be used 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.
- MUST be used 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.
- MUST be used only if `idsLength` is different from `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](#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](#actions-and-subjects) involved.
### Actions and subjects
The main actions that can be performed within a token are:
- **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_.
The subjects outlined above are expected to exhaustively represent _what_ can go wrong in a token transaction, deriving a specific error by adding an [error prefix](#error-prefixes).
Note that the action is never seen as the subject of an error. Additionally, the token itself is not seen as the subject of an error but rather the context in which it happens, as identified in the domain.
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:
```solidity
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:
```solidity
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](#arguments).
## 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](#domain).
- using different [error prefixes](#error-prefixes).
- including similar [subjects](#actions-and-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
```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 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 ERC1155InsufficientApproval(address operator, uint256 tokenId);
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.
## Copyright
Copyright and related rights waived via [CC0](../LICENSE.md).