DeFi 阶段四 ✅ 验证通过
ERC20是以太坊上最广泛使用的代币标准(EIP-20),由Fabian Vogelsteller于2015年提出。它定义了同质化代币(Fungible Token)的通用接口。
// IERC20.sol — ERC20标准接口
interface IERC20 {
// ═══════ 必须实现的6个函数 ═══════
/// @notice 代币总供应量
function totalSupply() external view returns (uint256);
/// @notice 查询账户余额
function balanceOf(address account) external view returns (uint256);
/// @notice 转账(直接转)
function transfer(address to, uint256 amount) external returns (bool);
/// @notice 查询授权额度
function allowance(address owner, address spender) external view returns (uint256);
/// @notice 授权(允许spender花我的钱)
function approve(address spender, uint256 amount) external returns (bool);
/// @notice 授权转账(由spender代替owner转账)
function transferFrom(address from, address to, uint256 amount) external returns (bool);
// ═══════ 2个事件 ═══════
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// contracts/SimpleERC20.sol — 从零实现
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract SimpleERC20 {
// ═══════ 状态变量 ═══════
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
address public owner;
// ═══════ 事件 ═══════
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// ═══════ 构造函数 ═══════
constructor(string memory _name, string memory _symbol, uint256 _initialSupply) {
name = _name;
symbol = _symbol;
decimals = 18;
owner = msg.sender;
// 铸造初始供应量给部署者
_mint(msg.sender, _initialSupply * 10 ** decimals);
}
// ═══════ 核心函数 ═══════
function transfer(address to, uint256 amount) external returns (bool) {
return _transfer(msg.sender, to, amount);
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
uint256 currentAllowance = allowance[from][msg.sender];
require(currentAllowance >= amount, "ERC20: insufficient allowance");
// 无需检查amount != type(uint256).max(无限授权不扣减)
if (currentAllowance != type(uint256).max) {
allowance[from][msg.sender] = currentAllowance - amount;
}
return _transfer(from, to, amount);
}
// ═══════ 内部函数 ═══════
function _transfer(address from, address to, uint256 amount) internal returns (bool) {
require(from != address(0), "ERC20: transfer from zero");
require(to != address(0), "ERC20: transfer to zero");
require(balanceOf[from] >= amount, "ERC20: insufficient balance");
balanceOf[from] -= amount;
balanceOf[to] += amount;
emit Transfer(from, to, amount);
return true;
}
function _mint(address to, uint256 amount) internal {
require(to != address(0), "ERC20: mint to zero");
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal {
require(from != address(0), "ERC20: burn from zero");
require(balanceOf[from] >= amount, "ERC20: burn exceeds balance");
balanceOf[from] -= amount;
totalSupply -= amount;
emit Transfer(from, address(0), amount);
}
}
// contracts/DeFiToken.sol — 完整DeFi代币
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract DeFiToken is ERC20, ERC20Burnable, ERC20Pausable, Ownable {
uint256 public maxSupply;
constructor(
string memory _name,
string memory _symbol,
uint256 _initialSupply,
uint256 _maxSupply
) ERC20(_name, _symbol) Ownable(msg.sender) {
maxSupply = _maxSupply * 10 ** decimals();
_mint(msg.sender, _initialSupply * 10 ** decimals());
}
// 所有者铸造新代币
function mint(address to, uint256 amount) external onlyOwner {
require(
totalSupply() + amount <= maxSupply,
"Exceeds max supply"
);
_mint(to, amount);
}
// 暂停所有代币转账
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
// 重写内部钩子函数
function _update(address from, address to, uint256 value)
internal
override(ERC20, ERC20Pausable)
{
super._update(from, to, value);
}
}
// 传统approve需要2笔交易: approve + transferFrom
// Permit只需1笔: 链下签名 + 合约内permit+transferFrom
// EIP-2612 Permit接口
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v, bytes32 r, bytes32 s
) external;
// 前端使用:一笔交易完成授权+转账
// 1. 用户在链下签名(MetaMask弹出一次)
// 2. DApp调用permit+transferFrom(一笔交易)
// → 省Gas、省时间、更好的UX!
| 代币 | 类型 | 供应量 | 特点 |
|---|---|---|---|
| USDT | 稳定币 | 830亿+ | 中心化法币抵押,Tether发行 |
| USDC | 稳定币 | 330亿+ | Circle+Coinbase,合规透明 |
| UNI | 治理代币 | 10亿 | Uniswap治理,4年线性释放 |
| LINK | 效用代币 | 10亿 | 预言机节点质押+支付 |
| AAVE | 治理+效用 | 1600万 | 借贷协议治理,可质押获安全模块收益 |
| COMP | 治理代币 | 1000万 | Compound治理,按借款量分配 |
1. ERC20的approve/transferFrom机制解决什么问题?
2. approve的竞态条件是什么?
3. EIP-2612 Permit的优势是什么?
// ERC4626 = 代币化金库标准(Vault)
// 存入底层代币 → 获得金库份额代币 → 赚取收益
interface IERC4626 is IERC20 {
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
function totalAssets() external view returns (uint256);
function convertToShares(uint256 assets) external view returns (uint256);
function convertToAssets(uint256 shares) external view returns (uint256);
// 存入100 USDC → 获得95 vaultToken → 收益后95 vaultToken = 110 USDC
}
你已掌握ERC20代币标准!从接口规范到从零实现,从授权机制到安全审计,你拥有了创建和管理同质化代币的完整能力。
关键收获:
✅ ERC20六大函数与两大事件
✅ approve/transferFrom授权机制与竞态条件
✅ 从零实现完整ERC20代币
✅ 扩展功能:Burnable、Pausable、Permit
✅ 代币安全审计要点