# 用Solidity构造ERC20代币 **Published by:** [LeaF.eth](https://paragraph.com/@leaf-eth/) **Published on:** 2022-11-30 **URL:** https://paragraph.com/@leaf-eth/solidity-erc20 ## Content 0x01 ERC20简介ERC20 token是以太坊用于制造同质化代币(Fungible Token)的协议,每一个token都是等于其他token的。ERC20 token可以用于交换货币、投票权、质押等媒介。 OpenZeppelin提供了许多ERC20关联合约,详见https://docs.openzeppelin.com/contracts/4.x/erc200x02ERC20合约解读合约链接https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol1、FunctionstotalSupply() 返回代币总供给function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } balanceOf(address account) → uint256 返回账户拥有的剩余tokenfunction 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; } 2、Events定义在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); 0x03 构建ERC20代币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。在Remix上部署智能合约并铸造100个Token查看当前账户余额有100个Token默认情况下ERC20使用小数点后18位,如果需要更改,可以重写decimals()function decimals() public view virtual override returns (uint8) { return 16; } 0x04 Token供应1、修改供给数在OpenZeppelin v1中使用如下方法修改totalSupply的数量。contract ERC20FixedSupply is ERC20 { constructor() { totalSupply += 1000; balances[msg.sender] += 1000; } } 但我们必须手动使修改后的totalSupply与修改后的余额保持同步。于是V2改进使用了封装的方法。_totalSupply和_balance被设为私有的,不能直接写入。\_balance和\_totalSupply于是V2开始使用了一个internal _mint() 函数来做此操作contract ERC20FixedSupply is ERC20 { constructor() ERC20("Fixed", "FIX") { _mint(msg.sender, 1000); } } 2、挖矿奖励在 Solidity 中,我们可以在全局变量 block.coinbase中访问当前区块的矿工地址。我们在只要访问mintMinerReward() 就可以获得挖token的奖励。contract ERC20WithMinerReward is ERC20 { constructor() ERC20("Reward", "RWD") {} function mintMinerReward() public { _mint(block.coinbase, 1000); } } 3、模块化机制在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); } } 4、自动化奖励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); } } ## Publication Information - [LeaF.eth](https://paragraph.com/@leaf-eth/): Publication homepage - [All Posts](https://paragraph.com/@leaf-eth/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@leaf-eth): Subscribe to updates