Standards: ERC-20, ERC-721, ERC-1155
Ethereum has over 500,000 token contracts, yet any wallet instantly shows your balance of each one, and any DEX lets you swap between them without special integration. How is this possible when every token is a separate contract written by different teams? The secret lies in three standards - ERC-20, ERC-721, and ERC-1155 - sets of functions and events that turn half a million contracts into a single, interoperable ecosystem. Without them, DeFi, NFT marketplaces, and blockchain games simply would not exist.
- **USDC (Circle)** - a stablecoin on ERC-20 with Pausable and Blacklistable extensions. In 2023, Circle froze $63M in USDC on addresses subject to OFAC sanctions - made possible by the extensible ERC-20 + Pausable architecture. Any DEX works with USDC without special integration because the contract conforms to the standard
- **Bored Ape Yacht Club (BAYC)** - a collection of 10,000 ERC-721 NFTs that reached a $4B market cap. Each tokenId is a unique ape with attributes stored in metadata (tokenURI → IPFS JSON). Marketplaces like OpenSea, Blur, and LooksRare trade them without integrating the specific contract - ERC-721 support is sufficient
- **Enjin / The Sandbox** - blockchain games using ERC-1155 for thousands of item types in a single contract. Batch operations allow distributing starter packs of 20 items in one call instead of 20 separate transactions, saving players hundreds of dollars in gas
Предварительные знания
ERC-20: Fungible Tokens
**ERC-20** is the standard for fungible tokens on Ethereum, proposed by Fabian Vogelsteller in November 2015. "Fungible" means every token is identical to another: 1 USDC = 1 USDC, just like 1 dollar = 1 dollar. The standard defines an **interface of 6 functions and 2 events** that a contract must implement so wallets, DEXs, and other contracts can interact with it in a uniform way.
The key pattern of ERC-20 is the **two-step transfer via approve + transferFrom**. When you want a DEX (like Uniswap) to spend your tokens, you can't simply send them to the contract - the contract must **pull** them itself. So you first call `approve(uniswapRouter, amount)`, and then the Uniswap contract calls `transferFrom(you, pool, amount)`.
**Infinite approval** - the common practice of calling `approve(spender, type(uint256).max)` to avoid confirming every transaction. But this creates enormous risk: if the spender contract is hacked or has a backdoor, it can drain **all** your tokens. In 2023, over $200M was stolen through approve vulnerabilities. Use exact approval amounts or the ERC-2612 (permit) standard for gasless approvals.
Real-world ERC-20 examples: **USDC** (stablecoin, 6 decimals instead of the standard 18), **WETH** (wrapped ETH for ERC-20 compatibility), **UNI** (Uniswap governance token), **LINK** (Chainlink utility token). Use OpenZeppelin's `SafeERC20` for calls - some tokens (USDT) don't return `bool` from `transfer`, which breaks standard checks.
Why does interacting with a DEX (Uniswap, Sushiswap) use the two-step approve + transferFrom pattern instead of a simple transfer?
ERC-721: Non-Fungible Tokens (NFT)
**ERC-721** is the standard for non-fungible tokens (NFTs), ratified in January 2018. Unlike ERC-20 where all tokens are identical, every ERC-721 token is **unique** and identified by its `tokenId` (uint256). One contract - one collection, where each tokenId has a single owner. CryptoKitties - the first mass-market ERC-721 project - congested Ethereum so badly in 2017 that it became clear a formal standard was needed.
The key difference from ERC-20 is that ERC-721 has `safeTransferFrom`, which checks whether the receiver can handle NFTs. If the receiving contract doesn't implement `IERC721Receiver`, the token will be permanently **locked** inside it. This is exactly how thousands of NFTs were lost when sent to regular contracts via `transferFrom`.
**ERC721Enumerable** adds the ability to list all tokens and all tokens of an owner, but significantly increases gas on mint and transfer (extra storage writes). For collections with thousands of NFTs, this can cost 50-100% more gas. The alternative is **ERC721A** by Azuki, which optimizes batch minting: minting 5 NFTs costs nearly the same as minting 1.
A vault contract does not implement the IERC721Receiver interface. What happens when safeTransferFrom is called to send an NFT to this contract?
ERC-1155: Multi-Token Standard
**ERC-1155** is the multi-token standard proposed by the Enjin team in 2018. The core idea: **one contract manages multiple token types simultaneously** - both fungible (like ERC-20) and non-fungible (like ERC-721). This is perfect for game items: a sword can exist as 1 unique piece (NFT) while a health potion can exist in 10,000 copies (fungible).
The main advantage of ERC-1155 is **batch operations**. Instead of 10 separate transactions to transfer 10 different tokens, you send a single `safeBatchTransferFrom`. Each Ethereum transaction costs at least 21,000 gas in base overhead, so batching can save **up to 80% gas** in bulk operations.
ERC-1155 has no per-token `approve` - only `setApprovalForAll`. This simplifies UX (one approval instead of ten), but means it's all-or-nothing: the operator gains access to **all** token types owned by the user. For URI, a `{id}` template pattern is used - the client replaces `{id}` with the zero-padded 64-character hex representation of tokenId: `https://game.example.com/api/item/0000...0004.json`.
A game wants to give 500 players a starter pack: 100 gold, 10 potions, and 1 unique sword. Why is ERC-1155 significantly more efficient than separate ERC-20 + ERC-721 contracts?
Comparing Standards and New ERCs
The three core token standards - ERC-20, ERC-721, and ERC-1155 - cover the vast majority of use cases. But choosing between them depends on the specific task, and beyond these three there are newer standards that address the shortcomings of the originals.
Beyond the three core standards, the Ethereum community has created several improvements that address specific problems. **ERC-777** attempted to replace ERC-20 by adding hooks (automatic callbacks when receiving tokens) and operators - but failed due to a critical **reentrancy vulnerability**.
**Why did ERC-777 fail?** The `tokensReceived` hook was called via the ERC-1820 Registry on the receiver *before* balance updates completed. An attacker could recursively call the withdrawal function inside the hook (reentrancy). This is how **Imbtc** on Uniswap V1 was exploited in 2020, with ~$300K stolen. After that, the community effectively abandoned ERC-777 in favor of ERC-2612 permit.
What is the key problem with the ERC-777 standard that led the community to abandon it?
Extensions and Advanced Standards
OpenZeppelin provides a set of **extensions** for base token standards that add commonly needed functionality. Instead of writing logic from scratch (and risking bugs), developers inherit battle-tested extensions - following the **composition over inheritance** principle via mixins.
**Wrapped tokens** solve compatibility problems. ETH is the native currency of Ethereum but doesn't conform to ERC-20. The **WETH** (Wrapped ETH) contract lets you wrap ETH into an ERC-20 token: deposit 1 ETH, receive 1 WETH (and vice versa). Similarly, **WBTC** is Bitcoin wrapped into ERC-20 via a custodian (BitGo).
**ERC-6551 (Token Bound Accounts)** shifts the paradigm: an NFT transforms from a passive asset into an **active participant** on the blockchain. Every NFT gets its own deterministic address (via CREATE2) that can hold tokens, send transactions, and interact with DeFi. When an NFT is transferred to a new owner, all contents of the account move with it - because only the NFT owner can control the bound account.
**How to choose extensions for your token:** Burnable - if you need deflation or a buy-and-burn mechanism. Pausable - if you need an emergency stop (regulated tokens, stablecoins). Permit (ERC-2612) - if you want UX without a separate approve transaction. Votes - if the token is used for governance (DAO). FlashMint - if you want to provide flash loans. Snapshot - if you need to capture balances at a specific block (for airdrops, votes).
ERC-1155 is just a combination of ERC-20 and ERC-721 in one contract, with no fundamental differences. You could achieve the same result by deploying separate ERC-20 and ERC-721 contracts
ERC-1155 has fundamental architectural differences: batch operations (balanceOfBatch, safeBatchTransferFrom), a single approval for all token types via setApprovalForAll, a single URI template with {id} substitution, and a unified callback interface IERC1155Receiver. Separate ERC-20 + ERC-721 contracts cannot atomically transfer different token types in a single call, require separate approvals for each contract, and are significantly more expensive in gas for bulk operations
This misconception arises from a surface-level understanding: since ERC-1155 supports both fungible and non-fungible tokens, it must just be ERC-20 + ERC-721 combined. But in practice the difference is not just quantitative (less gas) - it's qualitative: batch operations enable atomic trades, mass airdrops, and complex game mechanics that are impossible or economically impractical with separate contracts. One safeBatchTransferFrom replaces dozens of individual transactions.
What happens to the Token Bound Account (ERC-6551) of an NFT character when that NFT is sold on a marketplace to another user?
Key Takeaways
- **ERC-20** defines 6 functions and 2 events for fungible tokens. The key pattern is the two-step transfer via `approve` + `transferFrom`, which allows contracts (DEXs, lending protocols) to atomically pull tokens on behalf of a user. OpenZeppelin's `SafeERC20` solves the problem of tokens that don't return `bool`
- **ERC-721** adds uniqueness via `tokenId` and `safeTransferFrom` to protect NFTs from being lost in contracts without `IERC721Receiver`. Metadata is stored off-chain (IPFS/HTTP) and referenced via `tokenURI`. `ERC721Enumerable` allows enumerating tokens but increases gas on mint
- **ERC-1155** combines fungible and non-fungible tokens in one contract with batch operations (`mintBatch`, `safeBatchTransferFrom`), saving up to 80% gas on bulk operations. A single `setApprovalForAll` replaces many separate approvals
- **ERC-777** failed due to reentrancy through hooks, but its ideas evolved into **ERC-2612** (gasless approve via permit signatures) and **ERC-4626** (standard interface for yield-bearing vaults)
- **New standards** extend capabilities: **ERC-5192** (soul-bound tokens) makes tokens non-transferable, while **ERC-6551** (token bound accounts) turns every NFT into a full smart account - and it's this compatible, extensible architecture that allows half a million contracts to function as a unified ecosystem
Related Topics
ERC standards are interfaces written in Solidity, implemented through inheritance patterns, and used as building blocks of DeFi protocols:
- Solidity: Language Basics — Interfaces, inheritance, modifiers, and events - the core Solidity constructs that all ERC standards are built upon
- Solidity Patterns — Checks-Effects-Interactions (against reentrancy in ERC-777), Proxy (for upgradeable tokens), Factory (for deploying tokens from factories)
- AMM: Automated Market Makers — DEX pools (Uniswap, Curve) are the primary consumers of the ERC-20 approve + transferFrom pattern and often issue their own LP tokens in ERC-20 format
- Upgradeable Contracts — USDC, USDT, and other major tokens use the proxy pattern to upgrade ERC-20 logic without losing state (balances, allowances)
Вопросы для размышления
- ERC-20 lacks safe transfers, and millions of dollars have been lost sending tokens to contracts without a withdrawal function. Why hasn't the community added safeTransfer to ERC-20 the way ERC-721 and ERC-1155 did? What are the trade-offs between backward compatibility and security?
- Soul-bound tokens (ERC-5192) prevent transfer - but what if the owner loses their wallet key? How could you implement a recovery mechanism for non-transferable tokens without violating their 'soul-binding' principle?
- ERC-6551 lets NFTs own other assets. What new business models and game mechanics does this unlock? How does an NFT's pricing change when its account holds ETH or other tokens?