# 用Solidity构造ERC20代币

By [LeaF.eth](https://paragraph.com/@leaf-eth) · 2022-11-30

---

0x01 ERC20简介
------------

ERC20 token是以太坊用于制造同质化代币（Fungible Token）的协议，每一个token都是等于其他token的。ERC20 token可以用于交换货币、投票权、质押等媒介。

OpenZeppelin提供了许多ERC20关联合约，详见[https://docs.openzeppelin.com/contracts/4.x/erc20](https://docs.openzeppelin.com/contracts/4.x/erc20)

0x02ERC20合约解读
-------------

合约链接[https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)

### 1、Functions

**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;
        }
    

### 2、Events

定义在IREC20.sol中[https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.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](https://storage.googleapis.com/papyrus_images/d6fac067b70188325a25440f70b1c2080706deb68792d6e8541dff0225fffda1.png)

在Remix上部署智能合约并铸造100个Token

![查看当前账户余额有100个Token](https://storage.googleapis.com/papyrus_images/4fb0eca08a6857caef02fe8b03315d6d7968e7a6413f58509a6035aa31c736dd.png)

查看当前账户余额有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](https://storage.googleapis.com/papyrus_images/77ed572e4fba294608261eaa6c52ef123e5d23eef88365bdf0840cd3a9065532.png)

\\\_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);
        }
    }

---

*Originally published on [LeaF.eth](https://paragraph.com/@leaf-eth/solidity-erc20)*
