以太坊标准代币系列之同质化代币ERC20

773 阅读10分钟

0x00 写在前面

众所周知,以太坊在现阶段最大的应用就是令牌发行,而在以太坊中有很多类型的令牌,最著名的当属ERC20了,但是对于其他几种令牌类型,可能还有一些朋友不知道,所以最近规划了一个系列,就是以太坊标准令牌系列。

目前市面上,凡是基于以太坊的令牌,在交易所上线交易的均是ERC20令牌,那么今天我们就来聊聊ERC20令牌的标准方案吧。

0x01 ERC20标准制定动机

在基于以太坊发行令牌时,如果各个令牌发行方都使用自有标准去发行令牌,那对于钱包开发者以及交易所对接成本是极其高昂了,因为他们需要为每一种令牌去独立进行对接,为了降低钱包开发者以及交易所的对接成本,以太坊社区制定了一个关于令牌的发行标准。

该标准中主要包含了,令牌的转移,地址余额的获取等方法。

0x02 ERC20标准规范

一个令牌的合约,一般需要令牌的发行量,每个地址的余额,令牌的转移等方法, 而ERC20标准就是将这些最常用又是必不可少的方法,对此进行标准化,方便开发者进行令牌合约的开发,也方便钱包开发者以及交易所对接成本降到最低。

其中,定义了三类,方法、属性、事件。

下面介绍这些标准方法:

A. 方法(Method)

1、totalSupply:令牌总量

函数原型
  1. function totalSupply() constant returns (uint256 totalSupply)

方法 该方法用于获取令牌总发行量

2、balanceOf:获取余额
函数原型
  1. function balanceOf(address _owner) constant returns (uint256 balance)

方法 该方法用于获取地址  _owner 的令牌余额

3、transfer:转账

函数原型
  1. function transfer(address _to, uint256 _value) returns (bool success)

方法 该方法用于将调用令牌合约的地址中的  _value个令牌转给  _to 地址

4、transferFrom:转账

函数原型
  1. function transferFrom(address _from, address _to, uint256 _value) returns (bool success)

方法 该方法用于从  _from 地址中发送  _value 个令牌给  _to地址。

当你希望能够使用其他智能合约控制你的令牌转移,则可以使用 transferFrom方法,而使用  transferFrom方法有一个前提条件,那就是需要调用者需要得到  _from 地址的授权,才可以进行令牌转移的操作。而如何进行授权,我们接下来会介绍。

5、approve:授权

函数原型
  1. function approve(address _spender, uint256 _value) returns (bool success)

方法 允许  _spender 地址从你的账户中转移  _value 个令牌到任何地方。

当你设置了一个 _value 之后,  _spender 地址可以分任何多次将令牌进行转移,直至  _value为0.

6、allowance:获取被授权令牌余额

函数原型
  1. function allowance(address _owner, address _spender) constant returns (uint256 remaining)

方法 获取  _owner 地址授权给  _spender 地址可以转移的令牌的余额。

B. 事件(Event)

事件是EVM内置的日志功能,而且在DAPP中,我们可以通过JS来监听事件的回调。在ERC-20令牌中,定义了以下事件:

1、Transfer:转移令牌
事件定义
  1. event Transfer(address indexed _from, address indexed _to, uint256 _value)

当进行令牌转移时,需要触发调用该事件。其中记录了令牌发送者 _from,令牌接受者  _to ,令牌发送量  _value.

2、Approval:授权事件
事件定义
  1. event Approval(address indexed _owner, address indexed _spender, uint256 _value)

当进行授权时,需要触发调用该事件,其中记录了授权者 _owner,被授权者  _spender,授权令牌量  _value

0x03 ERC20令牌接口

通过以上的标准,我们可以了解到一个遵守ERC20令牌标准的令牌合约需要实现的方法,以下是通过规范实现的一个ERC20令牌的接口。

  1. contract EIP20Interface {

  2.    ///////////////////////////// 方法 ///////////////////////////////////

  3.      // 获取令牌发行量

  4.    // 注意:constant的修饰符是提示该方法中不能进行变量的修改。

  5.    //      但编译器不会强制校验。

  6.    // @return uint256 totalSupply 总发行量

  7.    function totalSupply() constant returns (uint256 totalSupply)

  8.       // 获取指定地址

  9.    // @param address _owner 想要获取的地址

  10.    // @return uint256 balance 令牌余额

  11.    function balanceOf(address _owner) public view returns (uint256 balance);

  12.    // 从`msg.sender`中发送`_value`个令牌给`_to`

  13.    // @param address _to 接收令牌的地址

  14.    // @param uint256 _value 发送的令牌数量

  15.    // @return bool success 发送令牌成功状态

  16.    function transfer(address _to, uint256 _value) public returns (bool success);

  17.    // 从`_from`地址发送`_value`令牌到`_to`地址

  18.    // 需要满足条件,需要被`_from`地址授权给`msg.sender`

  19.    // @param address _from 发送者地址

  20.    // @param address _to 接收者地址

  21.    // @param uint256 _value 发送的令牌数量

  22.    // @return bool success 发送令牌成功状态

  23.    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);

  24.    // `msg.sender`授权`_spender`地址可以任意转移`_value`数量的令牌

  25.    // @param address _spender 被授权发送令牌的地址

  26.    // @param uint256 _value 授权发送令牌的数量

  27.    // @return bool success 授权成功状态

  28.    function approve(address _spender, uint256 _value) public returns (bool success);

  29.    /// @param _owner The address of the account owning tokens

  30.    /// @param _spender The address of the account able to transfer the tokens

  31.    /// @return Amount of remaining tokens allowed to spent

  32.    // 获取被授权限额

  33.    // @param address _owner 令牌所有者的账户

  34.    // @param address _spender 令牌被授权者账户

  35.    // @return uint256 remaining 剩余可发送的令牌数量

  36.    function allowance(address _owner, address _spender) public view returns (uint256 remaining);

  37.    ///////////////////////////// 事件 ///////////////////////////////////

  38.    // 令牌转移事件 当发生令牌的转移时,需要调用该事件

  39.    event Transfer(address indexed _from, address indexed _to, uint256 _value);

  40.    // 授权转移事件 当进行授权时,需要触发该事件

  41.    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

  42. }

0x04 ERC20代码示例

在标准的基础上,有很多代码的实现方法,比如有的在代码中实现空投,有的在实现锁定,有的实现挖矿等等,但是最常规的实现已经有了官方的实现代码,也有很多组织实现了一些范例,如果没有定制化的地方,完全可以直接采用这些代码去创建ERC20令牌。

示例代码:

  1. pragma solidity ^0.4.18;

  2. contract ERC20 {

  3.    // 定义一个mapping 存储各个地址的令牌

  4.    mapping (address => uint256) public balances;

  5.    // 定义一个mapping 存储授权详情

  6.    mapping (address => mapping (address => uint256)) public allowed;

  7.    // 以下参数非必须,但是尽量添加,大多数钱包会通过此获取相关信息

  8.    // 令牌发行总量

  9.    uint256 public totalSupply;

  10.    // 令牌名称,如 OmiseGO

  11.    string public name;      

  12.    // 支持的小数位数

  13.    // 因为在EVM中对浮点数的支持很差,在令牌的创建中直接采用整数

  14.    // 然后通过该字段进行小数的处理

  15.    uint8 public decimals;                

  16.    // 令牌简称,如 OMG

  17.    string public symbol;              

  18.    // 令牌合约的构造函数

  19.    // 在solidity中,和合约名称一致的方法为构造函数,在第一次创建合约时,仅执行一次。

  20.    function ERC20(

  21.        uint256 _totalSupply,    // 令牌创建总量

  22.        string _name,        // 令牌名称

  23.        uint8 _decimals,    // 支持小数位数

  24.        string _symbol        // 令牌简称

  25.    ) public {

  26.        // 给创建者账户初始化令牌

  27.        balances[msg.sender] = _totalSupply;              

  28.        // 设置令牌发行量

  29.        totalSupply = _totalSupply;                        

  30.        // 设置令牌名称

  31.        name = _name;                                  

  32.        // 设置令牌支持小数位数

  33.        decimals = _decimals;                            

  34.        // 设置令牌简称

  35.        symbol = _symbol;                              

  36.    }

  37.    /**

  38.     * 获取令牌总发行量

  39.     * @return uint256 totalSupply 发行总量

  40.     */

  41.    function totalSupply() constant returns (uint256 totalSupply) {

  42.        return totalSupply;

  43.    }

  44.    /**

  45.     * 转移令牌

  46.     * @param address _to 令牌接收者地址      

  47.     * @param uint256 _value 发送令牌数量

  48.     * @return bool success 发送成功状态

  49.     */

  50.    function transfer(address _to, uint256 _value) public returns (bool success) {

  51.        // 判断发送者余额是否充足

  52.        require(balances[msg.sender] >= _value);

  53.        // 从发送者余额中减去`_value`数量的令牌

  54.        balances[msg.sender] -= _value;

  55.        // 给接收者账户中添加`_value`数量的令牌

  56.        balances[_to] += _value;

  57.        // 记录令牌转移的事件

  58.        Transfer(msg.sender, _to, _value);

  59.        return true;

  60.    }

  61.    /**

  62.     * 转移令牌(授权)

  63.     * @param address _from 令牌发送者地址

  64.     * @param address _to 令牌接收者地址

  65.     * @param uint256 _value 令牌发送数量

  66.     * @return bool success 方法执行状态

  67.     */

  68.    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {

  69.        // 其中`msg.sender`是合约方法调用者的地址

  70.        // 获取`_from`地址授权给`msg.sender`地址的可转移令牌余额

  71.        uint256 allowance = allowed[_from][msg.sender];

  72.        // 判断`_from`地址余额是否充足以及授权转移令牌是否充足

  73.        require(balances[_from] >= _value && allowance >= _value);

  74.        // 从`_from`地址减去`_value`数量的令牌

  75.        balances[_from] -= _value;

  76.        // 从授权余额中减去`_value`数量的令牌

  77.        allowed[_from][msg.sender] -= _value;

  78.        // 给`_to`地址添加`_value`数量的令牌

  79.        balances[_to] += _value;

  80.        // 记录转移令牌的事件

  81.        Transfer(_from, _to, _value);

  82.        return true;

  83.    }

  84.    /**

  85.     * 获取`_owner`地址的令牌余额

  86.     * @param address _owner 要获取余额的地址

  87.     * @return uint256 balance 返回`_owner`地址的余额

  88.     */

  89.    function balanceOf(address _owner) public view returns (uint256 balance) {

  90.        return balances[_owner];

  91.    }  

  92.    /**

  93.     * 授权令牌转移方法

  94.     * @param address _spender 被授权地址

  95.     * @param uint256 _value 授权可转移的数量

  96.     * @return bool success 方法执行状态

  97.     */

  98.    function approve(address _spender, uint256 _value) public returns (bool success) {

  99.        // `msg.sender`授权`_spender`地址转移`_value`数量的令牌

  100.        allowed[msg.sender][_spender] = _value;

  101.        // 记录授权事件

  102.        Approval(msg.sender, _spender, _value);

  103.        return true;

  104.    }

  105.    /**

  106.     * 获取被授权的令牌余额

  107.     * @param address _owner 授权地址

  108.     * @param address _spender 被授权地址        

  109.     * @return uint256 remaining 可转移的令牌余额

  110.     */

  111.    function allowance(address _owner, address _spender) public view returns (uint256 remaining) {

  112.        return allowed[_owner][_spender];

  113.    }  

  114. }

0x05 写在后面

大多数在交易所交易的令牌的合约代码就这么简单,其实每一个方法拆分开来都是最简单的编程代码,而核心的处理都被EVM进行了封装,以太坊在令牌发行方面确实极大的解放了人类,简单几十行代码就可以发行一个令牌。ERC20令牌又被成为同质化令牌,就是每个令牌都是一致的,无法区分,而市场上现在冒出了很多以太猫,以太狗的游戏,而这里面也是使用以太坊的令牌来实现的,但是他们选择的不是ERC20令牌,而是被成为非同质化的令牌,被称为ERC721令牌。

下期,我们一起来聊非同质化令牌ERC721。

喜欢,不要说话,扫我~