SOURCE CODE

IERC6093.sol

EIP-6093 standardized custom errors inherited by EffortToken. Every error explained.Because developers of the future should know the code they interact with.

Interactive Walkthrough
Solidity0.8.20
|
LicenseMIT
Inherits
Lines45
Verified on Etherscan
IERC6093.sol
1// SPDX-License-Identifier: MIT
2// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)

SPDX-MIT license and the OpenZeppelin version comment. The path interfaces/draft-IERC6093.sol tells you this is an interface file and the standard is still in draft. The "I" prefix is a naming convention borrowed from C#/Java indicating this is an interface, not a concrete contract.

  • MIT license allows unrestricted use, modification, and distribution
  • The "draft-" prefix will be removed if/when EIP-6093 reaches Final status in the EIP process
  • This is the same v5.0.0 used by all other contracts in the EffortToken dependency tree
3pragma solidity ^0.8.20;

Same ^0.8.20 pragma as EffortToken and Ownable. All files in a compilation unit must be compatible with the same compiler version. Custom errors (used throughout this file) require Solidity 0.8.4+, so the 0.8.20 floor is more than sufficient.

Standardized Errors: Why EIP-6093 Exists

EIP-6093 defines standardized custom error types for token contracts. Before this standard, every project invented its own error messages (or worse, used raw require strings), making it impossible for wallets, block explorers, and debugging tools to provide consistent error decoding. This file lives in OpenZeppelin's interfaces/ directory and carries a "draft" prefix because EIP-6093 was still in draft status when v5.0.0 shipped. The interface keyword means these are pure declarations with no implementation code — they define WHAT errors exist, not WHEN they are thrown.

Why it matters

Standardized errors are the foundation of a good developer experience. When EffortToken reverts with ERC20InsufficientBalance, any wallet (MetaMask, Rabby, etc.) or block explorer can decode that error into a human-readable message without needing the contract's ABI. Before EIP-6093, a transfer() failure might show as a raw hex revert, leaving users and developers guessing.

Security Note

The "draft" status does NOT mean the code is unsafe — OpenZeppelin shipped it as production-ready in v5.0.0. "Draft" in EIP terms means the standard is still open for community feedback, not that the implementation is experimental. The code itself is fully audited.

Tips & Tricks
  • Custom errors were introduced in Solidity 0.8.4 — they cost roughly 50 gas less than require("string message") because they don't store the string in bytecode.
  • The file path interfaces/draft-IERC6093.sol follows OpenZeppelin's naming convention: "I" prefix for interfaces, "draft-" prefix for standards still in draft.
  • This is one of the few OpenZeppelin files with zero executable code — it's pure type definitions that other contracts inherit.
Common Mistakes
  • Defining your own custom errors for standard operations like insufficient balance — use the EIP-6093 errors instead so tooling can decode them.
  • Thinking "draft" means unstable or untested — in EIP terminology it just means the standard hasn't been finalized, but the code is production-quality.
Key Terms
EIP-6093
An Ethereum Improvement Proposal that standardizes custom error types for ERC-20, ERC-721, and ERC-1155 tokens. Provides consistent error signatures across all conforming contracts.
Custom Error
A Solidity feature (since 0.8.4) that replaces require() string messages with structured, gas-efficient error types. Errors are encoded as 4-byte selectors plus ABI-encoded parameters.
Interface
A Solidity construct containing only function signatures, error declarations, or event declarations — no state variables, no implementations, no constructors.
💡
Did You Know?

Before EIP-6093, a failed ERC-20 transfer might show as "execution reverted: ERC20: transfer amount exceeds balance" (a raw string costing ~200+ gas per character in bytecode) or just raw hex. EIP-6093 replaced all of these with structured errors that cost less gas AND are machine-readable.

IERC6093.sol
5/**
6 * @dev Standard ERC20 Errors
7 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
8 */

The documentation block links directly to the EIP-6093 specification page. The NatSpec uses Solidity's documentation comment style (/** */), and the link syntax https://...[ERC-6093] is a standard NatSpec URL reference format.

  • The link points to the official EIP page on ethereum.org
  • "Standard" in the title means these errors are the CANONICAL set — all EIP-6093 compliant tokens use these exact definitions
9interface IERC20Errors {

Declares a Solidity interface containing only error declarations. Interfaces cannot have state variables or function implementations. The "I" prefix is a naming convention indicating this is an interface, not a concrete contract. ERC20.sol inherits this to get all 6 error types.

  • Interfaces compile to zero bytecode — they only define type signatures
  • Any contract that is IERC20Errors can use these error types in revert statements
  • EffortToken gets these through its ERC20 inheritance chain: EffortToken → ERC20 → IERC20Errors
10 error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

Thrown when a transfer or transferFrom is attempted with an amount exceeding the sender's balance. The three parameters give full context: WHO tried to send, HOW MUCH they have, and HOW MUCH was needed. This is the error you'll see most often when testing EffortToken.

  • Triggered in ERC20._update() when the from-address balance is less than the amount
  • In EffortToken: try to transfer 100 EFFORT when you only have 50, and you'll get ERC20InsufficientBalance(yourAddress, 50, 100)
  • The "needed" parameter is the total amount required, not the shortfall
11 error ERC20InvalidSender(address sender);

Thrown when the "from" address in a transfer is the zero address (address(0)). This prevents minting from being disguised as a transfer. The ERC-20 standard treats transfers from address(0) as invalid — minting has its own separate logic.

  • Guards against: transfer(from: address(0), to: someAddress, amount)
  • This is NOT about unauthorized senders — it's specifically about the zero address
  • Minting uses _mint() which explicitly handles the from=address(0) case separately
12 error ERC20InvalidReceiver(address receiver);

Thrown when the "to" address in a transfer is the zero address. This prevents accidental token burning via transfer. If you want to burn tokens, use the explicit _burn() function instead.

  • Guards against: transfer(from: someAddress, to: address(0), amount)
  • Prevents accidental loss: sending tokens to address(0) is irreversible
  • EffortToken's spendTokensForContent uses _burn() which handles the to=address(0) case explicitly
13 error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

Thrown when transferFrom() is called but the spender hasn't been approved for enough tokens. Parameters show the spender, their current allowance, and the amount they tried to spend. This is the second most common ERC-20 error after InsufficientBalance.

  • Triggered when: allowance[owner][spender] < amount in transferFrom()
  • Fix: the token owner must call approve(spender, amount) first
  • The "needed" parameter is the full transfer amount, not just the shortfall beyond the current allowance
14 error ERC20InvalidApprover(address approver);

Thrown when the address calling approve() is the zero address. This is a sanity check that prevents approval records from being created with an invalid owner.

  • Catches edge cases in internal _approve() calls where the owner parameter is address(0)
  • In normal usage, the approver is msg.sender and this error rarely fires
  • Different from InvalidSender — this is about approval, not transfer
15 error ERC20InvalidSpender(address spender);

Thrown when approve() is called with the spender set to the zero address. You cannot approve the zero address to spend tokens — it would create a meaningless allowance record.

  • Guards: approve(spender: address(0), amount) from succeeding
  • Prevents wasted gas on approvals that could never be used (no one controls address(0))
  • This is the approve-side counterpart to ERC20InvalidReceiver on the transfer side
16}

Six errors cover every ERC-20 failure mode. Two for balance/allowance insufficiency (with amounts), two for invalid sender/receiver (transfer guards), and two for invalid approver/spender (approval guards). This symmetry is intentional — the EIP was designed so every operation has a matching error.

The Six Errors Your ERC-20 Token Can Throw

IERC20Errors defines exactly 6 custom errors covering every failure mode in the ERC-20 standard: insufficient balance on transfer, invalid sender (zero address), invalid receiver (zero address), insufficient allowance on transferFrom, invalid approver, and invalid spender. EffortToken's ERC20 base contract inherits all 6 of these. When you call transfer() and don't have enough tokens, the contract reverts with ERC20InsufficientBalance(yourAddress, yourBalance, amountNeeded) — giving you structured, decodable failure data.

Why it matters

These are the errors you will actually encounter when building dApps. Every failed transfer(), transferFrom(), or approve() on EffortToken reverts with one of these 6 errors. Understanding them is essential for building frontend error handling. With these standardized errors, wagmi's error decoding can automatically show users "You tried to send 100 EFFORT but only have 50" instead of a raw hex revert.

Security Note

The error parameters intentionally expose internal state (like the user's current balance and the required amount). This is safe because all of this data is already public on the blockchain. The parameters are designed for debugging, not secrecy.

Tips & Tricks
  • In your frontend, decode these errors with viem's decodeErrorResult or wagmi's built-in error handling — the 4-byte selector uniquely identifies each error.
  • Each error is named with an ERC20 prefix, making it impossible to confuse with ERC-721 or ERC-1155 errors even in multi-token contracts.
  • The parameter names (sender, balance, needed) match the EIP specification exactly — they serve as self-documentation.
  • InsufficientBalance and InsufficientAllowance both include the current amount AND the required amount, so users can see exactly how much they're short.
Common Mistakes
  • Not handling ERC20InsufficientAllowance separately from ERC20InsufficientBalance in your UI — they require different user actions (approve vs. acquire more tokens).
  • Confusing ERC20InvalidSender with ERC20InvalidApprover — InvalidSender is about transfer-from addresses, InvalidApprover is about who's calling approve().
  • Ignoring the error parameters and showing generic "transaction failed" — the whole point of EIP-6093 is to provide actionable error data.
Key Terms
ERC20InsufficientBalance
Thrown when transfer() or transferFrom() is called but the sender's balance is less than the amount. Parameters: sender address, current balance, needed amount.
ERC20InsufficientAllowance
Thrown when transferFrom() is called but the spender's allowance from the token owner is insufficient. Parameters: spender address, current allowance, needed amount.
4-byte selector
The first 4 bytes of the keccak256 hash of the error signature. Used by EVM clients to identify which error was thrown. Each of the 21 errors in this file has a unique selector.
💡
Did You Know?

ERC20InsufficientBalance is probably the single most-triggered custom error in all of Ethereum. Every time anyone on any EIP-6093-compliant token tries to send more than they have, this exact error fires. On mainnet alone, it happens thousands of times per day.

IERC6093.sol
18/**
19 * @dev Standard ERC721 Errors
20 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
21 */

Same documentation pattern as IERC20Errors, linking to the EIP-6093 specification. The comment explicitly says "ERC721 tokens" to differentiate from the ERC-20 and ERC-1155 sections.

22interface IERC721Errors {

Eight error declarations for all ERC-721 failure modes. Two more than ERC-20 because NFTs need per-token-ID error reporting and have the concept of operators (from setApprovalForAll).

23 error ERC721InvalidOwner(address owner);

Thrown when an NFT operation would result in the zero address becoming an owner. Similar to ERC20InvalidSender but specific to NFT ownership context.

  • Used in _mint() and _transfer() when the intended owner is address(0)
  • NOT about checking if someone IS the owner — that's IncorrectOwner
24 error ERC721NonexistentToken(uint256 tokenId);

Thrown when referencing a tokenId that hasn't been minted or has been burned. This error has no ERC-20 equivalent because fungible tokens don't have individual IDs.

  • Common in NFT marketplaces when a token is burned between listing and purchase
  • The uint256 tokenId tells you exactly which token was looked up and not found
25 error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

Thrown when the caller claims to own a specific token but doesn't. Three parameters provide full context: who tried (sender), which token (tokenId), and who actually owns it (owner).

  • The most parameter-rich error in the ERC-721 set — three pieces of data
  • Enables frontend messages like: "Token #42 is owned by 0xABC, not by you"
26 error ERC721InvalidSender(address sender);
27 error ERC721InvalidReceiver(address receiver);

Same zero-address guards as the ERC-20 equivalents, but for NFT transfers. InvalidSender prevents transfers from address(0); InvalidReceiver prevents transfers to address(0).

  • Mirror the ERC-20 pattern exactly — consistent naming across standards
28 error ERC721InsufficientApproval(address operator, uint256 tokenId);

Thrown when an operator tries to transfer an NFT they haven't been approved for. Unlike ERC-20's allowance-based system, NFT approval is per-token or per-operator, so the parameters are operator and tokenId (no "amount").

  • Two ways to get approved: approve(operator, tokenId) for one NFT, or setApprovalForAll(operator, true) for all
  • No "amount" parameter because NFTs are indivisible — either you're approved or you're not
29 error ERC721InvalidApprover(address approver);
30 error ERC721InvalidOperator(address operator);

Two approval-side guards. InvalidApprover catches approve() from address(0). InvalidOperator catches setApprovalForAll() with an invalid operator address.

  • InvalidOperator is unique to ERC-721 — ERC-20 has no equivalent because it doesn't have the operator/setApprovalForAll concept
31}

Eight errors covering ownership, existence, transfers, and approvals. The tokenId parameter threading is the key differentiator from ERC-20 errors.

NFT Errors: Token IDs Change Everything

IERC721Errors defines 8 custom errors for ERC-721 (NFT) operations. The key difference from ERC-20 errors is the presence of tokenId parameters — because NFTs are non-fungible, errors must identify WHICH specific token is involved. New error types like NonexistentToken and IncorrectOwner exist because NFTs have individual ownership tracking that fungible tokens don't need.

Why it matters

Even though EffortToken is an ERC-20, understanding ERC-721 errors builds a complete mental model of the token error ecosystem. Many dApps combine fungible and non-fungible tokens (e.g., an NFT marketplace that uses ERC-20 for payments). EIP-6093 ensures ALL token errors across ALL standards follow the same conventions, so your frontend error-handling code can be reused.

Security Note

ERC721IncorrectOwner takes three parameters (sender, tokenId, owner) — this is critical for frontends to distinguish between "you don't own this NFT" and "this NFT doesn't exist at all" (NonexistentToken). Confusing these two in your UI can mislead users.

Tips & Tricks
  • Notice the pattern: ERC-721 has 8 errors vs ERC-20's 6. The extra two (InvalidOwner and NonexistentToken) exist because NFTs have per-token ownership that fungible tokens lack.
  • ERC721InsufficientApproval replaces ERC20InsufficientAllowance — NFT approvals are per-token, not per-amount.
  • InvalidOperator is specific to ERC-721's setApprovalForAll — the equivalent concept doesn't exist in ERC-20.
Common Mistakes
  • Confusing ERC721InvalidOwner (owner is zero address) with ERC721IncorrectOwner (caller expected to own the token but doesn't).
  • Not checking for NonexistentToken before displaying NFT data — querying a burned or unminted tokenId triggers this error.
Key Terms
ERC-721
The standard for non-fungible tokens (NFTs). Each token has a unique tokenId and exactly one owner.
NonexistentToken
Thrown when an operation references a tokenId that hasn't been minted or has been burned.
setApprovalForAll
An ERC-721 function that approves an operator to manage ALL of an owner's NFTs, unlike single-token approve().
💡
Did You Know?

The ERC721IncorrectOwner error with its three parameters (sender, tokenId, owner) was one of the most debated additions in EIP-6093. The community argued about whether to include the actual owner in the error data — ultimately they decided the debugging benefit outweighed any privacy concern, since ownership is public on-chain anyway.

IERC6093.sol
33/**
34 * @dev Standard ERC1155 Errors
35 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
36 */

Third and final NatSpec block, linking to the same EIP-6093 spec. ERC-1155 is the newest of the three token standards, finalized in 2018.

37interface IERC1155Errors {

Seven errors for the most complex token standard. The error count (7) falls between ERC-20 (6) and ERC-721 (8), but includes the unique InvalidArrayLength for batch validation.

38 error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

Like ERC20InsufficientBalance but with a fourth parameter: tokenId. In ERC-1155, each token ID has its own balance, so the error must specify WHICH token's balance is insufficient. Parameters: sender, current balance, needed amount, token ID.

  • This is the only 4-parameter error in all of EIP-6093
  • In a game: "You have 3 of Sword#42 but tried to send 5" → ERC1155InsufficientBalance(you, 3, 5, 42)
39 error ERC1155InvalidSender(address sender);
40 error ERC1155InvalidReceiver(address receiver);

Same zero-address guards as ERC-20 and ERC-721. The consistency across all three standards is intentional — tooling can handle all three with the same pattern matching on the error prefix.

41 error ERC1155MissingApprovalForAll(address operator, address owner);

Thrown when an operator tries to transfer tokens without setApprovalForAll permission. Unlike ERC-721's per-token InsufficientApproval, ERC-1155 only has the blanket "all tokens" approval model.

  • Parameters: (operator, owner) — tells you WHO tried and WHOSE tokens they tried to move
  • No tokenId parameter because the approval covers ALL token IDs
  • Named "Missing" instead of "Insufficient" because approval is boolean (yes/no), not an amount
42 error ERC1155InvalidApprover(address approver);
43 error ERC1155InvalidOperator(address operator);

Approval-side zero-address guards. Same pattern as ERC-721 — prevents setting approvals with invalid addresses.

44 error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);

The most unique error in all of EIP-6093. Thrown when batch functions (safeBatchTransferFrom, etc.) receive ids[] and values[] arrays of different lengths. This is a structural validation error — not about balances or permissions, but about input correctness.

  • Parameters: idsLength and valuesLength — shows the mismatch directly
  • Example: passing 3 token IDs but 5 amounts → ERC1155InvalidArrayLength(3, 5)
  • This error catches bugs at the contract boundary, before any state is modified
  • No equivalent exists in ERC-20 (no batching) or ERC-721 (batching isn't in the base standard)
45}

The file is complete. Three interfaces, 21 total custom errors, covering every failure mode across ERC-20, ERC-721, and ERC-1155. This is the entire error vocabulary of the token ecosystem.

Multi-Token Errors: When One Standard Handles Both

IERC1155Errors defines 7 custom errors for ERC-1155, the multi-token standard that can represent both fungible and non-fungible tokens in a single contract. The key additions are: InsufficientBalance now includes a tokenId parameter (because ERC-1155 tracks balances per-token-ID), MissingApprovalForAll replaces per-token approval with a blanket operator model, and InvalidArrayLength catches batch operation mismatches — a new error type not present in either ERC-20 or ERC-721.

Why it matters

ERC-1155 represents the evolution of token standards. It combines ERC-20's balance tracking with ERC-721's token IDs, and adds batch operations on top. Understanding its error types shows how the same error patterns (insufficient balance, invalid sender/receiver, missing approval) scale across increasingly complex token standards. The InvalidArrayLength error specifically highlights a new class of input validation that only exists in batch-capable contracts.

Security Note

The ERC1155InvalidArrayLength error is a critical batch safety check. Without it, mismatched ids[] and values[] arrays in batch transfers could lead to out-of-bounds access or silent data corruption. Always validate array lengths before iterating in batch operations.

Tips & Tricks
  • Compare across all three interfaces: InsufficientBalance has 3 params in ERC-20, 4 in ERC-1155 (adds tokenId). The pattern is consistent but adapted to each standard's needs.
  • ERC-1155 has no per-token approval — only setApprovalForAll. That's why it's MissingApprovalForAll instead of InsufficientApproval.
  • InvalidArrayLength is the only "structural" error in all of EIP-6093 — every other error is about authorization or balances.
  • Notice there's no NonexistentToken equivalent — ERC-1155 allows querying balance for any tokenId (returns 0 if never minted).
Common Mistakes
  • Passing mismatched ids and values arrays to batch functions — InvalidArrayLength will revert the entire batch.
  • Expecting per-token approval in ERC-1155 like ERC-721 — ERC-1155 only supports setApprovalForAll, which is all-or-nothing.
  • Thinking ERC-1155 balance of 0 means the token doesn't exist — unlike ERC-721, ERC-1155 doesn't track existence, only balances.
Key Terms
ERC-1155
The multi-token standard. A single contract can manage multiple token types (fungible and non-fungible) with batch transfer support.
Batch Operations
ERC-1155's ability to transfer multiple token types in a single transaction using arrays of IDs and amounts. Saves gas compared to individual ERC-20/721 transfers.
setApprovalForAll
The ONLY approval mechanism in ERC-1155. Approves an operator for ALL of an owner's tokens across all token IDs — there's no per-token-ID approval.
💡
Did You Know?

ERC-1155 was originally designed for gaming (by Enjin in 2018) where a single contract might manage thousands of item types — swords, shields, potions, currencies — each with different quantities. The InvalidArrayLength error is a direct consequence of this batch-first design philosophy.

IERC6093.sol — OpenZeppelin v5.0.0 on OP Sepolia

View on Etherscan