ERC-20 全景知识总结:标准、兼容性与真实世界的代币行为

152 阅读4分钟

本文适合以太坊工程师、SDK 开发者、Web3 技术学习者阅读。
目标是从标准 → 生态 → 实践 → SDK 设计的维度,全面理解 ERC-20。


1. 为什么需要 ERC-20?

ERC-20 是 EVM 生态中最重要的智能合约标准之一。它提供了一个统一的接口来表示可替代资产(Fungible Token),让所有钱包、交易所、DApp 都能以相同方式与代币交互。

ERC-20 的典型用途包括:

① 支付

  • DApp 内付费
  • 协议手续费
  • 游戏通证

② 资产表示

  • 稳定币(USDC、USDT)
  • 项目代币(治理/激励)
  • 跨链映射资产
  • 协议 LP Token / 借贷凭证

③ 权限控制(Access Control)

  • 代币持仓解锁权限
  • Staking 质押
  • DAO 投票权

ERC-20 是 Web3 价值流转的最小单位。

2. ERC-20 标准接口(EIP-20)

ERC-20 的规范来自 EIP-20,定义了每个代币必须实现的最小接口。

2.1 读取方法

function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);

2.2 写入方法

function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);

2.3 事件(非常关键)

event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);

事件在实际应用中具有极高重要性:

  • 浏览器需要事件解析转账记录
  • SDK 和 indexer 靠日志更新余额
  • 多数前端依赖事件来判定交易结果

没有事件的 ERC-20 难以在生态中正常工作。

3. 现实世界中“不完全标准”的 ERC-20

虽然有 EIP-20,但现实情况是:

许多代币(包括主流代币)并不完全符合标准。

例如:

  • USDT:transfer / approve 不返回 bool
  • 手续费代币:转账会自动扣手续费
  • rebasing token:余额会动态变化
  • 权限代币:可被冻结 / 黑名单
  • shares 模型:balanceOf 返回的是 share,而不是实际用户余额

3.1 常见的不标准行为分类

🎭 类别描述示例
no-return tokentransfer 不返回 boolUSDT
revert-without-reason错误时不返回 error message多数老代币
fee-on-transfer转账会扣手续费SafeMoon 类
rebasing token余额动态改变AMPL, aToken
shares tokenbalanceOf 返回 sharecToken
blacklist/pause管理者可冻结USDC(早期版本)
gas 优化行为无 revert message、行为非标若干旧代币

这是为什么很多 SDK 或协议并不能 100% 兼容所有 ERC-20。

4. SafeERC20:生态兼容问题的解决方案

OpenZeppelin 的 SafeERC20 是目前合约中唯一能安全处理所有“不标准 ERC-20”的方式。

其核心逻辑如下:

function _callOptionalReturn(IERC20 token, bytes memory data) private {
    (bool success, bytes memory returndata) = address(token).call(data);
    require(success, "ERC20: low-level call failed");

    // 如果代币有返回值,则必须是 true
    if (returndata.length > 0) {
        require(abi.decode(returndata, (bool)), "ERC20: operation did not succeed");
    }
}

SafeERC20 能解决:

✔ 代币不返回 bool

→ 按成功处理

✔ 代币返回 false

→ revert

✔ 可捕获各种不标准行为

→ 统一接口、降低错误率

SafeERC20 是智能合约层的事实标准。

5. ERC-20 的常见扩展标准

ERC-20 本身非常基础,但生态上已经出现许多扩展:

① EIP-2612 permit(最关键)

允许用户用签名授权,无需链上 approve。

用途:

  • Gasless approve
  • 改善用户体验
  • DEX swap 前不用额外发一次 approve

② EIP-3009 transferWithAuthorization

用于 meta-transaction 风格的“凭证转账”。

USDC、USDT 普遍支持。

③ EIP-20 Metadata(名称/符号/精度)

function name() external view returns (string);
function symbol() external view returns (string);
function decimals() external view returns (uint8);

6. ERC-20 的设计缺陷

ERC-20 在设计上存在一些长期累积的问题:

1. approve 的竞态漏洞(race condition)

必须先将 allowance 设置为 0,再设置新值。

2. transferFrom 与 fee-on-transfer 代币不兼容

用户允许 100,但只收到 95。

3. 无批量转账 / 无批量 allowance

后来才出现 permit2 与 multicall。

4. 无授权过期机制

permit 才解决了这个问题。

因此 ERC-20 在 DeFi 时代暴露出明显不足。

7. SDK / 客户端应如何正确处理 ERC-20?

✔ 1. 不依赖 transfer / approve 的返回值

因为许多代币不返回 bool。

✔ 2. 使用事件(logs)判断行为

✔ 3. 允许 fee-on-transfer / shares 模型的行为差异

✔ 4. 支持 permit(提升用户体验)

✔ 5. 支持自定义 decimals,而不是假设是 18

✔ 6. multicall 时要考虑 no-return token

✔ 7. 提供 fallback 的 ABI decode 逻辑

8. 总结

ERC-20 虽然看似简单,但完整理解这个标准需要掌握:

  • 标准接口(EIP-20)

  • 现实世界的偏差(七大不标准类型)

  • SafeERC20 的解决方案

  • 扩展标准(permit、EIP-3009 等)

  • SDK 和协议如何容错与兼容