ERC20
什么是ERC20?
就是一份技术规范(一键发币模板)。2017年定稿。从此任何钱包交易所与DApp都不再需要为每个新代币进行定制开发,直接用这个模板就行。
EIP-20(全称为 Ethereum Improvement Proposal 20),也常被称为 “ERC-20 标准”(ERC 即 Ethereum Request for Comments,是 EIP 被社区接受后的正式名称)。它定义了 fungible token(可替代代币)的统一接口规范,让不同代币在以太坊网络中能被一致地交互和管理。
内容
ERC20标准提供了name、symbol、decimals等元数据接口,用于描述代币的基本信息,如名称、符号和小数位数,方便用户识别和使用。
6个函数
- 总供应量
1 | function totalSupply() public view returns (uint256) |
- 转账(从自己账户直接转移代币)
1 | function transfer(address _to, uint256 _value) public returns (bool success) |
- 余额查询
1 | function balanceOf(address _owner) public view returns (uint256 balance) |
- 给第三方_spender授权最大额度
1 | function approve(address _spender, uint256 _value) public returns (bool success) |
- 转移授权(第三方用已经授权的地址从_from地址划扣代币_value代币转给_to,返回成功与否)
1 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) |
- 查询剩余授权额度
1 | function allowance(address _owner, address _spender) public view returns (uint256 remaining) |
2个事件
- 转账事件
- 授权事件
其他
- mint函数:mint函数由onlyOwner调用,为指定地址增加代币,可用于初始发行或后续增发,灵活控制代币供应
- burn函数:burn函数允许用户销毁自有代币,减少总供应量,可用于通缩机制或用户主动销毁。
- ERC20Capped供应上限设计是 OpenZeppelin 提供的一个 ERC20 扩展合约(重写_mint函数),它只在铸币时增加了一条检查:“如果新铸币数量会导致总供应量超过事先设定的上限,交易回滚并抛出 ERC20ExceededCap错误。”
1 | // SPDX-License-Identifier: MIT |
Ownable合约(OpenZeppelin v4.x 及以上版本)的构造函数需要接收一个initialOwner参数(默认值为msg.sender,但在多重继承时需显式确认)。
- ERC20Pausable是 OpenZeppelin 为 ERC-20 代币提供的一个「一键急停开关」扩展。在普通 ERC-20 的基础上增加了 暂停 / 恢复 机制——当合约被设为 paused 状态 时,任何转账、铸币、销毁操作都会被强制回滚,有效防止重大漏洞或黑天鹅事件造成更大损失。
1 | // SPDX-License-Identifier: MIT |
具体执行流程:
- 调用
super._update→ 先执行ERC20Pausable的_update(检查合约是否被暂停,若暂停则 revert)。- 若未暂停,
ERC20Pausable的_update会继续通过super调用ERC20原生的_update(处理余额变更、触发Transfer事件等核心逻辑)。- ERC20Snapshot快照功能设计是 OpenZeppelin为ERC-20代币提供的一个扩展。在任意指定区块高度(由管理员显式触发)记录那一刻所有账户的余额和代币总供应量,后续任何时刻都可以精准地回溯查询这些数据,而不会受之后的转账、铸币或销毁操作影响。
作用:
- 防止“一币多用”攻击:攻击者无法在快照后通过闪电贷或快速转账,把同一份代币重复用于投票、领空投或分红。
- 无需信任的分红 / 空投:按快照时的持仓比例发放奖励,杜绝后续刷量行为。
- 治理投票权重:把投票权固定在某一历史时刻的持仓快照上,避免投票期间“买票”或“闪转”干扰结果。
关于decimals
区块链上的数字都是整数(无法直接存储小数),但代币通常需要支持小额交易(比如转账 0.5 个代币)。为了解决这个问题,ERC20 标准引入了decimals(小数位数)的概念。
例如:
1 | function mint(address to, uint256 amount)external onlyOwner returns(bool){ |
例如:
假设代币的decimals = 2(默认常用 18):
- 用户调用
mint(to, 5),表示想铸造 5 个整数代币。 - 代码计算:
scaledAmount = 5 * (10^2) = 5 * 100 = 500。 - 最终合约会存储 “500 个最小单位”,等价于 “5 个整数代币”。
当用户查询余额时,合约返回的是最小单位数量,前端会自动除以10^decimals,展示为用户易懂的整数 + 小数形式(比如 500 → 5.00)
合约编写
OpenZeppelin 是一个开源的智能合约开发框架,专为以太坊及其他兼容 EVM 的区块链设计,提供经过安全审计、可重用、模块化的 Solidity 代码库,帮助开发者快速、安全地构建去中心化应用(DApp)和智能合约。
使用openzeppelin合约库:
1 | npm install @openzeppelin/contracts |
在线remix已经默认下载好了。
在合约里的体现:
1 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol" |
编写一个最基础的ERC20合约:
1 | // SPDX-License-Identifier: MIT |
提示:将_mint权限限制为onlyOwner,防止恶意铸造导致代币供应失控,维护代币经济模型稳定。
onlyOwner 也是OpenZeppelin提供的一个权限控制修饰符,定义在Ownable.sol合约中。如果不导入下面这个包,Solidity编译器不认识onlyOwner修饰符,会直接报错。
1 | import "@openzeppelin/contracts/access/Ownable.sol"; |
当然,自己写一个onlyOwner的函数修饰器也行。
WETH合约实现
概述
WETH(Wrapped Ether)合约是一种将原生以太坊(ETH) 转换为符合 ERC20 标准的代币的智能合约。
为什么要转换?
原生 ETH 是以太坊网络的基础货币,但其设计并不遵循 ERC20 标准(没有transfer、approve等 ERC20 标准函数)。
这导致了两个问题:1.无法直接参与ERC20生态。(许多 DeFi 协议的核心逻辑基于 ERC20 标准设计,ETH 无法直接与这些协议交互,例如无法作为交易的一方、无法被质押)2.功能限制。(ETH 没有 ERC20 的 allowance 授权机制,无法实现 “第三方代转账” 等功能,而这是许多 DeFi 操作的基础。)
WETH解决方案:通过deposit与withdraw实现ETH与WETH的双向兑换,解决了兼容性问题。
WETH9合约地址:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
函数接口设计
- deposit函数它无需参数,通过payable接收ETH,为调用者铸造等量WETH,实现把原生 ETH 1:1 封装成 ERC-20 形式的 WETH。
1 | function deposit() public payable { |
- withdraw函数是WETH合约的反向操作函数。withdraw函数销毁指定数量WETH并转回ETH,实现WETH到ETH的转换。这个函数传入的参数就是指销毁的WHTETH的数量(单位:wei)。
1 | function withdraw(uint wad) public { |
只要涉及转账一定要防止重入攻击。
WETH合约版本
目前常用的 WETH(Wrapped Ether)主要有 5 个版本(或实现),核心差异体现在 Solidity 版本、功能丰富度、Gas 效率、授权机制等方面。
注意:WETH9合约是“从零手搓”的 ERC-20 合约,它没有继承 ERC20、Ownable —— 所有状态变量和函数都在 WETH9 内显式声明。但是它依然兼容 ERC-20,它的接口与 ERC-20 完全一致,前端和 DeFi 协议可直接把它当 ERC-20 使用。



