Every ERC-20 token — USDC, DAI, LINK, UNI, LDO, thousands more — is a smart contract implementing the same six functions. Reading the ERC-20 standard is not just an exercise in blockchain literacy; it explains why token approvals are the #1 vector for wallet-draining scams, why gas fees for token operations are consistently higher than ETH transfers, and why "revoking approvals" is a habit every serious crypto user should build.
The standard itself is thirteen years old and remarkably small. Understanding it takes an hour and unlocks a huge fraction of what happens on-chain.
What ERC-20 is not
ERC-20 is not a token — it's a contract standard that describes how a token contract should behave. Every ERC-20 token is a smart contract with a standard interface, meaning any wallet or dApp can interact with any ERC-20 token using the same code.
Before ERC-20 (proposed in November 2015), every token had its own custom interface. Wallets had to write specific integration code for each. ERC-20 standardized this — a wallet that supports ERC-20 automatically supports any ERC-20 token, present or future. This standardization is why we have thousands of tokens circulating today; without it, the ecosystem would be balkanized.
The six required functions
An ERC-20 contract must implement:
- **totalSupply()** — returns the total token supply
- **balanceOf(address)** — returns the balance held by an address
- **transfer(to, amount)** — sender sends amount to recipient
- **approve(spender, amount)** — sender allows spender to spend up to amount from sender's balance
- **transferFrom(from, to, amount)** — spender moves amount from from to to (only works if approve was called first)
- **allowance(owner, spender)** — returns how much spender is still allowed to move from owner's balance
That's it. Two additional events (Transfer and Approval) are also required so external systems can watch for state changes.
Most tokens also implement decimals, name, symbol as optional metadata, but these aren't strictly required by the standard.
Why approve/transferFrom exists
The simple case — user Alice sends 100 USDC to user Bob — works via transfer(Bob, 100). Alice signs, the tokens move.
The complex case is DeFi. When Alice wants to deposit 100 USDC into a Compound money market, the deposit contract needs to move USDC out of Alice's wallet into the contract. But Alice can't just call Compound's deposit function — she doesn't have permission to send from Compound's address, and Compound doesn't have permission to send from hers.
The approve/transferFrom pattern solves this in two steps:
1. Alice calls USDC.approve(Compound, 100). This says "USDC contract, please note that Compound is allowed to move up to 100 USDC from my balance." 2. Alice calls Compound.deposit(100). Compound internally calls USDC.transferFrom(Alice, Compound, 100). Because Alice pre-approved this, USDC allows the transfer.
Every DeFi interaction (swap, deposit, borrow) follows this pattern. It's why every "first-time" interaction with a new protocol requires two transactions: one to approve, one to execute.
The unlimited approval problem
The natural approve pattern is: approve exactly the amount you want to spend. If you're depositing 100 USDC, approve exactly 100.
But wallets and dApps default to unlimited approvals. Why? Because otherwise every deposit requires a new approve transaction, doubling the gas cost. UX won.
An unlimited approval says: "spender can move as much as they want from my balance, forever, until I explicitly revoke."
This is the security problem. If the approved contract is later compromised (via a bug, an upgrade, a governance takeover), the attacker can drain everything the approval allows — which for unlimited approvals is your entire balance.
Historical examples:
- 2021: Nomad bridge hack; users who had approved the bridge for various tokens lost millions.
- 2022: Multichain hack; approvals to Multichain's router were used to steal funds.
- 2023: Ledger Connect Kit exploit; a compromised library injected malicious approval requests into wallet UX.
None of these hacks required breaking cryptography or stealing private keys. They exploited existing approvals.
The wallet drainer economy
Phishing attacks that exploit approvals are the single biggest ongoing threat to Ethereum users. The mechanism:
1. User visits a fake site (compromised Google ad, malicious Discord link, typosquatted URL). 2. Site prompts the user to "connect wallet" and then to approve one or more contract interactions. 3. The approval is for a malicious contract that has approval logic hiding behind a normal-looking function call. 4. The malicious contract then drains all tokens the user approved.
This works because:
- Wallet UIs historically showed approval requests without highlighting the risk (many still do).
- Users are habituated to approving contracts they don't understand.
- Chain analysis can't tell the difference between "user approving Uniswap" and "user approving drainer" until after the drain.
Wallet drainer as a service — turnkey infrastructure for phishing operators — is a real business. Estimates of user losses to drainers are in the hundreds of millions of dollars annually.
The Permit variant (EIP-2612)
Permit is a newer approval mechanism that avoids the two-transaction pattern. Instead of a separate on-chain approve transaction, the user signs an EIP-712 typed message off-chain that authorizes a specific spend. The dApp includes this signature in its actual transaction, and the token contract accepts it as authorization.
For UX, this is cleaner — one transaction instead of two. But permit has its own risks:
- **Permit signatures are hard to interpret in wallet UIs**. A typed message hash doesn't look like anything meaningful; users often approve without knowing what they're authorizing.
- **Permit approvals are still approvals**. A malicious dApp can extract a permit signature and use it exactly like a normal approval.
- **Not all tokens support permit**. Only ERC-2612-implementing tokens (USDC, DAI, and many newer tokens) support it; older tokens like USDT don't.
Wallets like Rabby and Frame show detailed permit-signature previews. This helps, but the underlying risk remains.
Revoking approvals
You can (and should) revoke approvals you no longer need. Tools:
- **Revoke.cash**: the most-used revocation tool. Shows every approval you've granted across your addresses, lets you revoke them one by one or in batch.
- **Etherscan**: has a Token Approvals tab per address showing approvals.
- **DeBank**: shows approvals in the security section of an address.
A monthly revocation habit is a small but meaningful security improvement. Every unnecessary approval is an attack surface.
The gas cost of revoking is roughly the same as making an approval — a few dollars on L1, cents on L2s. Batch tools help; the marginal cost per revocation drops significantly if you revoke many at once.
Reading a token on Etherscan
Any ERC-20 token has a contract address. On Etherscan, that page shows:
- **Contract tab**: the source code (if verified). Look for the six standard functions plus whatever else the token adds. Many tokens add mint(), burn(), or governance functions.
- **Holders tab**: who owns how much. Useful for spotting concentration or unusual holders.
- **Transactions**: who's transferring the token, in what patterns.
- **Read/Write Contract**: query state and (if you connect a wallet) directly call functions.
Every legitimate token has a verified contract on Etherscan. If you're looking at a token whose contract isn't verified, be extremely cautious — you can't tell what it actually does.
Gas cost implications
ERC-20 operations are more expensive than ETH transfers because they involve storage writes and state changes in a smart contract, not just a balance update:
- ETH transfer: 21,000 gas.
- ERC-20 transfer: 45,000-65,000 gas.
- ERC-20 approve: 45,000-60,000 gas.
- ERC-20 transferFrom (called by a dApp): 40,000-90,000 gas plus the dApp's own logic.
This is why token operations at high gas prices can be surprisingly expensive. A simple token swap on Uniswap might cost 200,000 gas total, which at 40 gwei is over 0.008 ETH ($20+ at $2,500/ETH). L2s dramatically reduce this — the same operation on Arbitrum or Base is typically under $0.50.
What ERC-20 doesn't give you
The standard covers the basics but leaves gaps:
- **No native metadata standard**: name and symbol are conventions, not required.
- **No native pausability**: some tokens (USDC) implement it as an extension, but it's not standardized.
- **No native access control**: mint/burn functions are token-specific and vary widely.
- **No native governance hooks**: DAO-controlled tokens implement voting in their own way.
Extensions like ERC-2612 (permit), ERC-3009 (transfer with authorization), and ERC-4626 (tokenized vaults) fill some gaps. But the core ERC-20 standard has stayed remarkably stable — most tokens today are still basically the same six functions plus a few extensions.
Understanding ERC-20 is understanding tokens on Ethereum. Everything from stablecoins to DeFi to governance tokens to memecoins is built on this pattern. Once you can read a token contract on Etherscan and understand the approve/transferFrom flow, you can read almost anything in DeFi.




