W3
EIP-20ERC代币标准Final(2015-11-19)

可替代代币标准

同质化代币的最小接口:6 个函数 + 2 个事件。稳定币、治理币、LP 代币、质押凭证几乎都由它派生。

适用链:全部 EVM 链 + 各 L2

为什么重要:因为接口统一,钱包、交易所、区块浏览器、DEX 才能「一套代码对接所有代币」。看懂 ERC-20 是看懂整个 DeFi 的前置条件:approve 与 transferFrom 的机制就是后来一切授权钓鱼的根源。

函数签名与选择器

选择器是签名的 keccak256 前 4 字节;事件则是完整的 32 字节 topic0。 点一下即可复制。

签名 / 字段选择器 / topic0说明
totalSupply()当前流通总量。若支持增发/销毁则是动态值。
balanceOf(address)查询某地址余额。注意它链上没有隐私性。
transfer(address,uint256)从调用者转出。返回 bool,但不少老币不返回或永远返回 true。
approve(address,uint256)授权 spender 可动用不超过 value 的额度,是覆盖而非累加。
allowance(address,address)查询剩余授权额度,撤销授权的检查依据。
transferFrom(address,address,uint256)代扣:从 from 转给 to,消耗调用者的授权额度。
event Transfer(address,address,uint256)事件前两个参数 indexed,铸造用 from = address(0),销毁用 to = address(0)。
event Approval(address,address,uint256)事件owner 授权 spender。索引器靠它重建授权清单。

代码模板

最小可用实现(零依赖,可直接编译部署)
最小可用实现(零依赖,可直接编译部署)Solidity

decimals 必须是常量且为 18 之外的值时要在文档里写明(USDC 是 6)。前端硬编码 18 会把 1 USDC 显示成 0.000001。

用 ethers v6 读余额与授权
用 ethers v6 读余额与授权JavaScript

实现与使用时的常见陷阱

  • approve 是覆盖写,不是累加。把 100 覆盖成 50 之前,老代码里「先置 0 再设值」的双交易写法是为了绕过某些老币的竞态保护。
  • 兼容非标准代币:有些币(BNB 早期的 BEP-20 实现)transfer 不返回 bool,直接 require(token.transfer()) 会失败。要判断返回数据长度。
  • 不要用 transfer 给合约地址转账后指望它有回调——ERC-20 没有钩子,转进去就是永久锁死。
  • 无限授权是最大的安全隐患。定期用区块浏览器或授权管理工具检查并撤销。
  • 不要依赖 decimals() 的调用结果做链下硬编码以外的假设,代币不会在同一交易里改它,但代理合约理论上可以。

相关标准

同属「代币标准」的其他标准
想把这个标准用起来?开发者路径的第 1–2 阶段讲了怎么把它写进项目;合约 IDE里有对应的可编辑模板与静态自检;链上 API 参考则给出前端调用它的三种写法。