彻底搞懂 ERC-20 代币标准:从接口源码到高级实践

·

核心关键词:ERC-20 代币、以太坊代币标准、智能合约、同质化代币、ABI 接口、代币丢失、Web3 查询


1. 为什么需要 ERC-20?——以太坊的水电煤

在以太坊上发行资产非常容易,但“各玩各的”代币互不兼容。为了让交易所、钱包、DeFi 协议快速集成每一种新代币,社区于 2015 年推出 ERC-20 代币标准。它像一把通用螺丝刀:

正因有了这套规范,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)授权钱包、交易所常用

核心事件


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——代币就会永久锁住。更糟糕的是,链上永远查得到这笔转账记录,但接收方源码中并无提币逻辑。

常见误入黑名单的地址类型

👉 一键查看黑客与散户都踩过的币圈“黑洞地址”列表


6. 如何避免踩坑

  1. 先验证地址:发币前用 Etherscan「Read Contract」确认对方账户具备 transferwithdraw 公开函数。
  2. 小额测试:大额分成两笔,第一笔 0.01 枚成功后再发剩余额度。
  3. 借助工具:使用钱包插件、批量转账脚本前先走本地 fork。Hardhat/Foundry 允许离线模拟。
  4. 升级标准:新项目考虑 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 的安全保险。把本文收藏,下次遇到 同质化代币、接口调试或 代币丢失 场景,可直接定位答案,少踩坑、多赚钱。