ERC20 #
ERC20
是以太坊上的代币标准,来自2015年11月V神参与的 EIP20
。你可以把实现了 ERC20
的智能合约理解为区块链上的一家银行。我们去银行办的业务无非就是查询账户余额、转账等。ERC20
就定义了区块链上的一家银行应该具备哪些能力。
如果你要在区块链上开一家 ERC20
银行,那么你的银行需要有以下业务能力:
- 查询代币总供给(totalSupply())
- 查询账户余额(balanceOf())
- 转账(transfer())
- 授权(approve())
- 查询授权转账额度(allowance())
- 授权转账(transferFrom())
- 代币信息(可选):名称(name()),代号(symbol()),小数位数(decimals())
IERC20 #
IERC20
是ERC20
代币标准的接口合约,规定了ERC20
代币需要实现的函数和事件。 之所以需要定义接口,是因为有了规范后,就存在所有的ERC20
代币都通用的函数名称,输入参数,输出参数。 在接口函数中,只需要定义函数名称,输入参数,输出参数,并不关心函数内部如何实现。 由此,函数就分为内部和外部两个内容,一个重点是实现,另一个是对外接口,约定共同数据。 这就是为什么需要ERC20.sol
和IERC20.sol
两个文件实现一个合约。
事件 #
IERC20
定义了2
个事件:Transfer
事件和Approval
事件,分别在转账和授权时被释放。
/**
* @dev 释放条件:当 `value` 单位的货币从账户 (`from`) 转账到另一账户 (`to`)时.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev 释放条件:当 `value` 单位的货币从账户 (`owner`) 授权给另一账户 (`spender`)时.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
函数 #
IERC20
定义了6
个函数,提供了转移代币的基本功能,并允许代币获得批准,以便其他链上第三方使用。
/**
* @dev 返回代币总供给.
*/
function totalSupply() external view returns (uint256);
/**
* @dev 返回账户`account`所持有的代币数.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev 转账`amount`单位代币,从调用者账户到另一账户`to`
* 如果成功,返回 `true`
* 释放 {Transfer} 事件
*/
function transfer(address to, uint256 amount) external view returns (bool);
/**
* @dev 调用者账户向`spender`授权`amount`单位代币
* 如果成功,返回 `true`
* 释放 {Approval} 事件
*/
function approve(address spender, uint256 amount) external view returns (bool);
/**
* @dev 返回`owner`向`spender`授权的额度
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev 从`from`账户向`to`账户转账`amount`单位代币
* 需要满足`from`已经向`to`账户授权超过`amount`单位代币
* 如果成功,`from`向`to`的`allowance`会扣除相应的额度,并返回`true`
*/
function transferFrom(address from, address to, uint256 amount) external view returns (bool);
实现 ERC20 #
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MyCoin is IERC20 {
string public name; // 名称 可选
string public symbol; // 代号 可选
uint8 public decimals = 18; // 小数位数 可选
/**
* 构造函数,初始化代币名称和代号
*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
// 代币总供给
uint256 public override totalSupply = 500;
// 账户-余额映射表
mapping(address => uint256) public override balanceOf;
// 账户-账户-授权额度映射表
mapping(address => mapping(address => uint256)) public override allowance;
/**
* @dev 转账`value`单位代币,从调用者账户到另一账户`to`
* 如果成功,返回 `true`
* 释放 {Transfer} 事件
*/
function transfer(address to, uint256 value) external override returns (bool) {
require(balanceOf[msg.sender] >= value);
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
return true;
}
/**
* @dev 调用者账户向`spender`授权`amount`单位代币
* 如果成功,返回 `true`
* 释放 {Approval} 事件
*/
function approve(address spender, uint256 amount) external override returns (bool) {
allowance[msg.sender][spender] += amount;
emit Approval(msg.sender, spender, amount);
return true;
}
/**
* @dev 从`owner`账户向`spender`账户转账`amount`单位代币
* 需要满足`owner`已经向`spender`账户授权超过`amount`单位代币
* 如果成功,`owner`向`spender`的`allowance`会扣除相应的额度,并返回`true`
*/
function transferFrom(address owner, address spender, uint256 amount) external override returns (bool) {
require(allowance[owner][spender] >= amount);
require(balanceOf[owner] >= amount);
allowance[owner][spender] -= amount;
balanceOf[owner] -= amount;
balanceOf[spender] += amount;
emit Transfer(owner, spender, amount);
return true;
}
}