核心关键词:ERC-20 代币、以太坊代币标准、智能合约、同质化代币、ABI 接口、代币丢失、Web3 查询
1. 为什么需要 ERC-20?——以太坊的水电煤
在以太坊上发行资产非常容易,但“各玩各的”代币互不兼容。为了让交易所、钱包、DeFi 协议快速集成每一种新代币,社区于 2015 年推出 ERC-20 代币标准。它像一把通用螺丝刀:
- 统一命名规则,不用每家都重写代码
- 确保同质化,1 枚代币 = 1 枚代币,方便计价
- 预定义调用接口,降低对接开发成本
正因有了这套规范,USDT、UNI、MKR 等上千种 以太坊代币 才能在同一条链上顺畅流通。
2. ERC-20 的六大必备接口
标准文档里最重要的部分,是以下 6 个 view 函数 与 3 个 write 函数 + 2 个 事件。只要合约实现它们,就算通过 ERC-20 认证。
| 功能 | 函数(语法示例) | 备注 |
|---|---|---|
| 名称 | function name() view returns (string) | 如 “Wrapped Ether” |
| 符号 | function symbol() view returns (string) | 通常是大写的 3–5 位,如 WETH |
| 小数位 | function decimals() view returns (uint8) | 值多为 18,与 ETH 保持一致 |
| 总发行量 | function totalSupply() view returns (uint256) | 决定了通证稀释比 |
| 账户余额 | function balanceOf(address) view returns (uint256) | – |
| 转账 | function transfer(to,value) returns (bool) | 直接链上转移 |
| 授权额度 | function approve(spender,value) returns (bool) | 让第三方合约代扣 |
| 查询授权额度 | function allowance(owner,spender) view returns (uint256) | – |
| 代为转账 | function transferFrom(from,to,value) returns (bool) | 授权钱包、交易所常用 |
核心事件
- Transfer:任何成功转账都必触发
- Approval:授权事件会被 DeFi 前端实时监听
3. 伪代码速查板
为了直观看接口,把上述函数用人类语言翻译一次。你可以把它当备忘录贴在工位上:
1. 我想知道这个代币名字 → name()
2. 我想知道 logo 大写简称 → symbol()
3. 小数点后几位 → decimals()
4. 总发行量有多少枚 → totalSupply()
5. 我的钱包还剩多少 → balanceOf(我的地址)
6. 我要把 100 枚发给你 → transfer(你的地址, 100e18)
7. 我允许 Uniswap 扣我 50 枚 → approve(Uniswap地址, 50e18)
8. 我还剩多少授权额度 → allowance(我的地址, Uniswap地址)4. 动手实验:用 Python 读取任意 ERC-20 代币数据
光说不练假把式。下面用 15 行代码,查询 DAI 与 WETH 的实时链上信息,不需要私钥,只要有 RPC Endpoint 即可。
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://cloudflare-eth.com"))
dai_addr = "0x6B175474E89094C44Da98b954EedeAC495271d0F"
weth_addr = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
test_acc = "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11"
# 简化版 ABI,仅保留 symbol、decimals、totalSupply、balanceOf
simplified_abi = [
{"inputs":[{"name":"account","type":"address"}],"name":"balanceOf","outputs":[{"type":"uint256"}],"type":"function","stateMutability":"view"},
{"inputs":[],"name":"decimals","outputs":[{"type":"uint8"}],"type":"function","stateMutability":"view"},
{"inputs":[],"name":"symbol","outputs":[{"type":"string"}],"type":"function","stateMutability":"view"},
{"inputs":[],"name":"totalSupply","outputs":[{"type":"uint256"}],"type":"function","stateMutability":"view"}
]
for addr in [dai_addr, weth_addr]:
contract = w3.eth.contract(address=w3.to_checksum_address(addr), abi=simplified_abi)
symbol = contract.functions.symbol().call()
decimals= contract.functions.decimals().call()
supply = contract.functions.totalSupply().call() / 10**decimals
bal = contract.functions.balanceOf(test_acc).call() / 10**decimals
print(f"===== {symbol} =====")
print("总发行量:", supply)
print("测试地址余额:", bal)执行后你会得到类似:
===== DAI =====
总发行量: 5353758461.008448
测试地址余额: 71231.520161678
===== WETH =====
总发行量: 2797067.1732031057
测试地址余额: 0.0👉 查看如何零代码查询链上实时数据,新手也能 3 分钟复现!
5. 代币丢失陷阱:为什么 200 多万美元的 WETH 进了黑洞?
把 ERC-20 代币当作礼物发到「顾不上的朋友地址」没问题,但若对方地址其实是个 合约——而这个合约又没实现 onERC20Received——代币就会永久锁住。更糟糕的是,链上永远查得到这笔转账记录,但接收方源码中并无提币逻辑。
常见误入黑名单的地址类型
- Layer2 桥接合约(未升级前)
- 老旧拍卖合约
- 未做边缘适配的 DAO 库房
6. 如何避免踩坑
- 先验证地址:发币前用 Etherscan「Read Contract」确认对方账户具备
transfer或withdraw公开函数。 - 小额测试:大额分成两笔,第一笔 0.01 枚成功后再发剩余额度。
- 借助工具:使用钱包插件、批量转账脚本前先走本地 fork。Hardhat/Foundry 允许离线模拟。
- 升级标准:新项目考虑 ERC-777 或 ERC-1155,它们天然兼容 hooks,可回调通知接收方。
7. 常见疑问答疑
Q1:ERC-20 和 ERC-721 到底区别在哪?
A:前者 ensured “1:1 等价”,适合货币化场景;后者代表独一无二的 NFT,每枚 tokenId 都不相同。
Q2:发一套代币,需要部署全部 9 个函数吗?
A:按标准必须全部实现,但 decimals 可以固定写死、name/symbol 写入构造函数即可通过验证。
Q3:我可以在 Uniswap 创建没有 ERC-20 标准的“山寨币”吗?
A:不行,DEX 列表会直接拒绝,钱包也无法识别余额,交易体验为 0。
Q4:私链也能用 ERC-20 吗?
A:当然可以,只要底层兼容 EVM。很多联盟链把 gas 费免了,手动 fork 一条即可内测。
Q5:怎样用 0 gas 领取空投?
A:依靠“元交易” relayer 机制,项目方替你付 gas,你只需离线签名;底层仍转移到你的 ERC-20 地址。
Q6:为什么有的代币交易一次会有自动销毁?
A:在合约层重写了 transfer 加入通缩逻辑,每次扣 1% 并销毁,仍是 ERC-20,只是更丰富。
8. 写在最后
ERC-20 标准已成为以太坊生态的 底层水煤电。无论你是开发者、投资者还是产品经理,理解它的接口语义、常见漏洞和升级路线,都是行走 Web3 的安全保险。把本文收藏,下次遇到 同质化代币、接口调试或 代币丢失 场景,可直接定位答案,少踩坑、多赚钱。