基于以太坊发行ERC20 Token(代币)

  • Post author:
  • Post category:其他


本文将介绍基于以太坊测试链,利用Remix与MetaMask两个工具发行Token(代币),以及基于ERC20标准规范编写代币合约,供初学者参考。


ERC20 Token


也许你经常看到ERC20和代币一同出现, ERC20是以太坊定义的一个代币标准。

要求我们在实现代币的时候必须要遵守的协议,如指定代币名称、总量、实现代币交易函数等,只有支持了协议才能被以太坊钱包支持。协议的github具体描述位于

https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md



一个标准的协议促使了代币可以在不同的应用中得到使用,如钱包和去中心化交易所。

接口定义如下:

pragma solidity ^0.4.19;

contract Token {
    /// token总量,默认会为public变量生成一个getter函数接口,名称为totalSupply().
    uint256 public totalSupply;

    /// 获取账户_owner拥有token的数量
    function balanceOf(address _owner) constant returns (uint256 balance);

    //从消息发送者账户中往_to账户转数量为_value的token
    function transfer(address _to, uint256 _value) returns (bool success);

    //从账户_from中往账户_to转数量为_value的token,与approve方法配合使用
    function transferFrom(address _from, address _to, uint256 _value) returns  (bool success);

    //消息发送账户设置账户_spender能从发送账户中转出数量为_value的token
    function approve(address _spender, uint256 _value) returns (bool success);

    //获取账户_spender可以从账户_owner中转出token的数量
    function allowance(address _owner, address _spender) constant returns  (uint256 remaining);

    //发生转账时必须要触发的事件 
    event Transfer(address indexed _from, address indexed _to, uint256 _value);

    //当函数approve(address _spender, uint256 _value)成功执行时必须触发的事件
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}


代币合约


基于ERC20编写的合约代码如下:

pragma solidity ^0.4.19;

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }

contract TokenERC20 {
    string public name;
    string public symbol;
    uint8 public decimals = 18;  // decimals 可以有的小数点个数,最小的代币单位。18 是建议的默认值
    uint256 public totalSupply;

    // 用mapping保存每个地址对应的余额
    mapping (address => uint256) public balanceOf;
    // 存储对账号的控制
    mapping (address => mapping (address => uint256)) public allowance;

    // 事件,用来通知客户端交易发生
    event Transfer(address indexed from, address indexed to, uint256 value);

    // 事件,用来通知客户端代币被消费
    event Burn(address indexed from, uint256 value);

    /**
     * 初始化构造
     */
    function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // 供应的份额,份额跟最小的代币单位有关,份额 = 币数 * 10 ** decimals。
        balanceOf[msg.sender] = totalSupply;                // 创建者拥有所有的代币
        name = tokenName;                                   // 代币名称
        symbol = tokenSymbol;                               // 代币符号
    }

    /**
     * 代币交易转移的内部实现
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // 确保目标地址不为0x0,因为0x0地址代表销毁
        require(_to != 0x0);
        // 检查发送者余额
        require(balanceOf[_from] >= _value);
        // 确保转移为正数个
        require(balanceOf[_to] + _value > balanceOf[_to]);

        // 以下用来检查交易,
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);

        // 用assert来检查代码逻辑。
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
     *  代币交易转移
     * 从自己(创建交易者)账号发送`_value`个代币到 `_to`账号
     *
     * @param _to 接收者地址
     * @param _value 转移数额
     */
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    /**
     * 账号之间代币交易转移
     * @param _from 发送者地址
     * @param _to 接收者地址
     * @param _value 转移数额
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    /**
     * 设置某个地址(合约)可以创建交易者名义花费的代币数。
     *
     * 允许发送者`_spender` 花费不多于 `_value` 个代币
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    /**
     * 设置允许一个地址(合约)以我(创建交易者)的名义可最多花费的代币数。
     *
     * @param _spender 被授权的地址(合约)
     * @param _value 最大可花费代币数
     * @param _extraData 发送给合约的附加数据
     */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            // 通知合约
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    /**
     * 销毁我(创建交易者)账户中指定个代币
     */
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        Burn(msg.sender, _value);
        return true;
    }

    /**
     * 销毁用户账户中指定个代币
     *
     * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
     *
     * @param _from the address of the sender
     * @param _value the amount of money to burn
     */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        Burn(_from, _value);
        return true;
    }
}


部署合约


完成代币合约编写后,可以将合约发布到Etherscan的测试链上(公链需要购买ether,而且速度较慢,建议开发在测试上进行),合约部署方式如下:

我采用Remix在线浏览器IDE才进行合约的编译和部署的,打开

Remix

页面样子如下:

image

点击新建按钮,将上面编辑的合约复制到IDE中:

image

复制完成后,IDE的左边会自动校验你的合约的编写是否准确,有错误会提示,警告可以忽略。

image

中间插入MetaMask讲解


MetaMask使用


由于我们是在以太坊测试链上发行合约,还是需要ether的,只不过可以免费获得,为了发行合约,我们需要测试链账户,下面我们将插入使用

MetaMask

chrome插件来链接创建账户:

image

安装好后,在浏览器右上角会出现图标,点击图标,一直点击到下图,填写你的密码,进入后就创建好了一个MetaMask钱包,MetaMask会为用户创建12个英文助记词,

一定要保存好这些助记词,一定要保存好这些助记词,一定要保存好这些助记词

,在其他钱包导入这个新创建的账户的时候有可能需要这些助记词。具体细节可以参考

这篇文章


image

创建好后,会给你默认一个账号:

image

由于我们是要基于以太坊测试链发行代币,所以我们选择测试链:

image

此时,我们发现我们的账户中没有ether,依次按照如下步骤获取,

image

image

这时候会打开网页,点击图标(建议点一两次就ok,每次会给你的账户放1个ether,部署合约1个就已经搓搓有余。)。

image

一会你就会发现你们账户有金额;

image

准备工作终于做完了,现在我们开始部署代币合约,这时候点击IDE右侧横栏中的run,按照1到3确认信息,并在4中编写你要发行的代币的信息,依次是100000000,”GaoTeB”,”GTB”(发行总量,发行币全称,发行币简称),4步确认不误后点击create按钮发布代币合约带测试链中。

image

点击后,会弹出对话框:

image

点击submit后,如果不报错,此时,会出现你的合约信息:

image

点击合约会打开页面,你可以看到正在创建中:

image

过一会,你就会看到:

image

那么你就基本已经发行成功你的代币了!

怎么在我们的账户下看呢?

下面我们使用MetaMask工具查看,依次点击:

image

将刚才打开的页面中的信息填入MetaMask中,

image

这时候,我们就能看到啦:

image

哎,终于大功告成,下面我们来转账一回试试:


代币交易


MetaMask插件没有提供代币交易功能,同时考虑到很多人并没有以太坊钱包或是被以太坊钱包网络同步问题折磨,今天我用

网页钱包

来讲解代币交易。

初次进入会有一些列的确认信息,一顿点击,来到这个页面,选择与MetaMask同一个网络链(以太坊测试链):

image

然后按下图点击:

image

点击链接后,这个页面就会和你的MetaMask链接上,你会发现你的账户信息就会出现在页面上。

此时,你的Token信息并没有出现在这里,需要你认为添加:

image

点击添加,将你的Token信息填入这里:

image

此时,你会发现:

image

然后开始代币交易,我们尝试转账给别的账户,填写好信息后依次点击:

image

此时,你将会发现,你的账户的Token已经减少了。

如果你觉得这边文章对你有些帮助,请wx给作者点辛苦费吧,谢谢!

image