ERC20 token是以太坊用于制造同质化代币(Fungible Token)的协议,每一个token都是等于其他token的。ERC20 token可以用于交换货币、投票权、质押等媒介。
OpenZeppelin提供了许多ERC20关联合约,详见https://docs.openzeppelin.com/contracts/4.x/erc20
合约链接https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
totalSupply()
返回代币总供给
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
balanceOf(address account) → uint256
返回账户拥有的剩余token
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
transfer(address to, uint amount) → bool
转账 amount 单位代币,从调用者账户到另一账户 to,成果则返回 true。释放{transfer}事件。
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
allowance(address owner, address spender) → uint256
返回owner对spender的授权额度,默认为0。当{approve} 或 {transferFrom} 被调用时,allowance会改变。
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
approve(address spender, uint256 amount) → bool
调用者账户给`spender`账户授权 `amount`数量代币,如果成果则返回true,释放Approval事件。
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
transferFrom(address from, address to, uint256 amount) → bool
从from地址向to地址转账amount数量token。转账的部分会从调用者的`allowance`中扣除。
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
定义在IREC20.sol中https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
Transfer(address from, address to, uint256 value)
当 ‘value’ 单位的货币从账户 (from) 转账到另一账户 (to)时被释放
event Transfer(address indexed from, address indexed to, uint256 value);
Approval(address indexed owner, address indexed spender, uint256 value)
当 ‘value’ 单位的货币从账户 (owner) 授权给另一账户 (spender)时被释放
event Approval(address indexed owner, address indexed spender, uint256 value);
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract YXYToken is ERC20{
constructor(uint256 _initalSupply) ERC20("LFToken","LFT"){
_mint(msg.sender,_initalSupply); //铸造token
}
}
铸造ERC20 Token通常需要使用继承方法。、
这里_initSupply代表初始化挖掘的Token数量,LFToken和LFT是我们ERC20 token的名称和缩写。_mint(msg.sender,_initalSupply) 表示对合约调用者铸造_initSupply 这么多个token。
如下图我们成果创建了100个LFT。


默认情况下ERC20使用小数点后18位,如果需要更改,可以重写decimals()
function decimals() public view virtual override returns (uint8) {
return 16;
}
在OpenZeppelin v1中使用如下方法修改totalSupply的数量。
contract ERC20FixedSupply is ERC20 {
constructor() {
totalSupply += 1000;
balances[msg.sender] += 1000;
}
}
但我们必须手动使修改后的totalSupply与修改后的余额保持同步。于是V2改进使用了封装的方法。_totalSupply和_balance被设为私有的,不能直接写入。

于是V2开始使用了一个internal _mint() 函数来做此操作
contract ERC20FixedSupply is ERC20 {
constructor() ERC20("Fixed", "FIX") {
_mint(msg.sender, 1000);
}
}
在 Solidity 中,我们可以在全局变量 block.coinbase中访问当前区块的矿工地址。我们在只要访问mintMinerReward() 就可以获得挖token的奖励。
contract ERC20WithMinerReward is ERC20 {
constructor() ERC20("Reward", "RWD") {}
function mintMinerReward() public {
_mint(block.coinbase, 1000);
}
}
在ERC20PresetMinterPauser 合约中已有包含。这是一种通用机制,其中一组帐户被分配了 minter 角色,授予他们调用 mint 函数(_mint 的外部版本)的权限。
这个机制可以被用在中心化挖矿中,一个外部账户决定给谁创建多少供应。这个机制有合法化的使用案例,就是传统资产支持稳定币(traditional asset-backed stablecoins)
contract MinerRewardMinter {
ERC20PresetMinterPauser _token;
constructor(ERC20PresetMinterPauser token) {
_token = token;
}
function mintMinerReward() public {
_token.mint(block.coinbase, 1000);
}
}
ERC20 还允许我们通过 _beforeTokenTransfer hook扩展代币的核心功能。我们可以使用hook为区块链中包含的每个代币传输铸造矿工奖励。
contract ERC20WithAutoMinerReward is ERC20 {
constructor() ERC20("Reward", "RWD") {}
function _mintMinerReward() internal {
_mint(block.coinbase, 1000);
}
function _beforeTokenTransfer(address from, address to, uint256 value) internal virtual override {
//判断调用函数的是创世区块,以及发送奖励的地址是矿工
if (!(from == address(0) && to == block.coinbase)) {
_mintMinerReward();
}
super._beforeTokenTransfer(from, to, value);
}
}

