Writing ERC-20 and ERC-721 Tokens in Solidity: Standards, Extensions, and Real-World Pitfalls

Writing ERC-20 and ERC-721 tokens in Solidity sits at the core of Ethereum and EVM development. ERC-20 powers most fungible assets in DeFi and payments, while ERC-721 remains the baseline for NFTs. Real deployments have surfaced recurring problems around security, gas costs, user experience, integration quirks, and compliance. These challenges have produced a practical ecosystem of extensions and patterns, including ERC-2612 (permit), ERC-777 (hooks), ERC-1155 (multi-token), ERC-2981 (royalties), ERC-4906 (metadata updates), and compliance-oriented standards like ERC-1400 and ERC-3643.
This guide focuses on implementation decisions, common pitfalls that continue to cause losses, and how to select the right standard for real-world constraints.

ERC-20 and ERC-721 Standards: What They Guarantee (and What They Do Not)
ERC-20 Overview for Fungible Tokens
ERC-20 defines the minimal interface for fungible tokens, where every unit is interchangeable. The core API revolves around balances, transfers, and allowances:
- Required: totalSupply, balanceOf, transfer, transferFrom, approve, allowance
- Common metadata: name, symbol, decimals
ERC-20 remains the dominant fungible standard for governance tokens, stablecoins, utility tokens, wrapped assets, and LP tokens across Ethereum and EVM-compatible chains.
ERC-721 Overview for NFTs
ERC-721 defines non-fungible tokens where each tokenId is unique. The standard specifies ownership queries, approvals, and transfer methods, including a safer transfer path for contracts:
- Ownership and balances: ownerOf, balanceOf
- Approvals: approve, setApprovalForAll, getApproved, isApprovedForAll
- Transfers: transferFrom, safeTransferFrom (with and without data)
Most production NFTs also adopt optional extensions for metadata such as tokenURI, and sometimes enumeration. ERC-721 remains foundational for digital collectibles, in-game items, access passes, and tokenized representations of real-world assets.
Related Standards You Will Likely Need
- ERC-1155: supports fungible, non-fungible, and semi-fungible tokens in one contract with batch transfers and better gas efficiency.
- ERC-777: introduces richer interactions and hooks for fungible tokens, improving composability but requiring careful security design.
- ERC-2612: adds permit for signature-based approvals to reduce UX friction.
- ERC-2981: standard interface for NFT royalty information (not enforcement).
- ERC-4906: metadata update signaling for NFTs.
- ERC-1400 / ERC-3643: compliance-aware standards for security tokens and regulated assets, adding transfer restrictions and identity or whitelist controls.
Writing ERC-20 in Solidity: Production Extensions and Pitfalls
Baseline ERC-20 Implementation Practices
Modern ERC-20 contracts typically rely on audited libraries like OpenZeppelin, use Solidity 0.8+ checked arithmetic, and strictly emit Transfer and Approval events. Audited baselines reduce integration risk and simplify formal review, which is why they are the starting point for most professional deployments.
Pitfall 1: ERC-20 Tokens Can Get Stuck in Contracts
A structural issue in ERC-20 is that transfers do not include receiver hooks. If a user sends ERC-20 tokens to a contract address that has no mechanism to handle or recover them, those tokens can become permanently unrecoverable. Ethereum.org has documented that at least $83,656,418 worth of ERC-20 tokens had been lost as of June 20, 2024 due to this exact issue.
Why it happens: transfer and transferFrom only update balances. They do not detect whether to is a contract, nor do they call a standardized receiver callback.
Mitigations you can implement or require at the application layer:
- Prefer well-tested transfer helpers in integrations and UIs that validate behavior and handle non-standard ERC-20 return values.
- Consider hook-based designs (ERC-777 style) when you need safe composability with contracts receiving tokens.
- UI and documentation safeguards: warn users about sending tokens to arbitrary contract addresses, especially non-custodial vaults or NFT contracts.
Pitfall 2: The Allowance and Approve Race Condition
ERC-20 allowances can create a known race condition when changing an allowance from a non-zero value directly to another non-zero value. A spender can potentially front-run and spend both the old and the new allowance depending on transaction ordering.
Best-practice patterns:
- Require a two-step flow: set allowance to 0, then set the new allowance.
- Use incremental allowance patterns (increase or decrease) at the application layer.
- Adopt ERC-2612 permit for signature-based approvals to reduce repeated approval friction.
Pitfall 3: Non-Standard Return Values Break Integrations
Some legacy tokens did not return a boolean from transfer or transferFrom despite the standard requirement, causing integration failures in DeFi protocols and middleware that assumed strict compliance. Modern libraries typically work around these inconsistencies, but your contract should remain spec-correct and your integration code should be written defensively.
Pitfall 4: Upgradeability and Proxy Risks
Many real ERC-20 deployments use proxy patterns for upgradeability. The risks are concrete:
- Storage layout collisions can corrupt balances or allowances after an upgrade.
- Admin key compromise can allow malicious upgrades.
- Tooling assumptions: some analytics platforms and integrations expect a direct implementation rather than a proxy.
If you need upgradeability, define an explicit governance and key management model and implement rigorous upgrade testing before any deployment.
ERC-20 Extension to Strongly Consider: ERC-2612 Permit
ERC-2612 adds permit, allowing allowances to be set using off-chain signatures. This removes the need for a separate on-chain approve transaction, improving onboarding and reducing gas costs for users. In DeFi and wallet-driven UX, permit is increasingly treated as a baseline feature rather than an optional enhancement.
ERC-777: Hooks and Richer Token Interactions (With Tradeoffs)
ERC-777 introduces hooks for senders and receivers, making token interactions more expressive and potentially safer for contract recipients. However, hooks and callbacks increase complexity and expand the reentrancy attack surface. Teams that adopt hooks should apply strict state-change ordering and reentrancy protection throughout.
Writing ERC-721 in Solidity: Extensions, Scalability, and Common Mistakes
Baseline ERC-721 Implementation Choices
In production, most ERC-721 contracts use OpenZeppelin's ERC-721 implementation with the metadata extension, exposing name, symbol, and tokenURI. The most critical operational decision is how you handle transfers to contracts.
Pitfall 1: Using transferFrom Can Lock NFTs
ERC-721 provides both transferFrom and safeTransferFrom. The safe variant checks whether the recipient is a contract and calls onERC721Received; it reverts if the receiver cannot handle NFTs. Using transferFrom to send an NFT to a contract that does not support ERC-721 reception can leave the NFT permanently stuck.
Best practice:
- Use safeTransferFrom in all user-facing flows.
- Restrict or discourage transferFrom usage in UIs unless you have a well-defined advanced use case that justifies it.
Pitfall 2: Metadata Centralization and Mutability
Many NFT projects keep metadata off-chain and mutable by updating a base URI. This can be legitimate for evolving games, but it creates trust and integrity concerns for collectors and integrators.
Implementation guidance:
- Define a clear metadata policy: mutable, frozen, or time-limited updates.
- Prefer content-addressed storage such as IPFS or Arweave for assets intended to be immutable.
- Consider ERC-4906 to signal metadata updates to indexers and marketplaces.
Pitfall 3: Enumeration Extension Gas Costs
The ERC-721 Enumerable extension can be expensive for large collections because it maintains on-chain tracking structures for token lists and indexes. High-volume collections often avoid full on-chain enumeration and rely on off-chain indexing services instead.
Pitfall 4: Royalty Fragmentation
Royalties are not part of base ERC-721. ERC-2981 provides a standardized way to expose royalty information, but enforcement depends on marketplace policies rather than on-chain guarantees. This gap affects real-world revenue expectations and how you communicate royalty mechanics to stakeholders.
ERC-20 vs ERC-721 vs ERC-1155: Choosing the Right Standard
Your choice should be driven by asset semantics and operational constraints, not familiarity with a particular standard.
- Choose ERC-20 for interchangeable units, payments, governance, rewards, and wrapped assets.
- Choose ERC-721 for unique assets with individual ownership and identity, such as collectibles, credentials, or unique game items.
- Choose ERC-1155 when you need both fungible and non-fungible assets, batch transfers, and inventory-style efficiency.
- Choose ERC-1400 or ERC-3643 when representing regulated assets that require compliance controls such as whitelists, transfer restrictions, or partitions.
Security Checklist: Pitfalls Shared Across ERC-20 and ERC-721
Regardless of standard, the following issues appear consistently in audits and incident reports:
- Reentrancy through hooks, callbacks, or external calls in transfer flows.
- Incorrect access control for minting, burning, pausing, and metadata changes.
- Unsafe external calls and missing failure handling, especially around receiver logic.
- Operational gaps: no emergency pause where one is needed, or unclear admin processes.
Conclusion: Treat ERC Standards as a Baseline, Not a Complete Product
Writing ERC-20 and ERC-721 tokens in Solidity is no longer about implementing the minimum interface. Real-world usage has demonstrated that small omissions in standards and integrations can produce large losses, as illustrated by the documented value of ERC-20 tokens permanently stuck in contracts. The most resilient projects use audited libraries, adopt UX-improving extensions like ERC-2612 permit, prefer safe transfer patterns for NFTs, and select newer standards like ERC-1155 or compliance frameworks like ERC-3643 when the use case demands it.
Designed from the perspective of interoperability, safety, and long-term maintainability, ERC-20 and ERC-721 remain excellent foundations. They work best when paired with modern extensions, defensive integration patterns, and a clear operational governance model.
Related Articles
View AllSolidity
Solidity Design Patterns for Web3 Apps: Access Control, Pausability, and Modular Architecture
Learn Solidity design patterns for access control, pausability, and modular architecture, including RBAC, governance admin, and upgrade-safe Web3 contract design.
Solidity
Solidity Events and Logs Explained: Building Indexable On-Chain Analytics for dApps
Learn how Solidity events and logs work, how indexed topics enable fast off-chain queries, and how to design event schemas for scalable dApp analytics.
Solidity
Upgradable Smart Contracts in Solidity: Proxy Patterns, Storage Layout, and Best Practices
Learn upgradable smart contracts in Solidity: transparent, UUPS, and beacon proxies, safe storage layout rules, initializer patterns, and secure upgrade governance.
Trending Articles
AWS Career Roadmap
A step-by-step guide to building a successful career in Amazon Web Services cloud computing.
How Blockchain Secures AI Data
Understand how blockchain technology is being applied to protect the integrity and security of AI training data.
Blockchain in Supply Chain Provenance Tracking
Supply chains are under pressure to prove not just efficiency, but also authenticity, sustainability, and fairness. Customers want to know if their coffee really is fair trade, if the diamonds are con