使用Remix与MetaMask发行以太坊ERC20 Token

ERC20是以太坊网络上发行代币(Token)的一个标准协议接口,协议的github具体描述位于https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md。一个标准的协议促使了代币可以在不同的应用中得到使用,如钱包和去中心化交易所。目前有很多实现该标准协议的Token Examples,我们将使用https://github.com/ConsenSys/Tokens提供的例子进行演示操作。我们先来看下这个实现库中的主要合约文件:

ERC20 Token协议实现

  1. Token.sol ERC 20协议的抽象定义
//Abstract contract for the full ERC 20 Token standard
// https://github.com/ethereum/EIPs/issues/20

pragma solidity ^0.4.8;
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);
}
  1. StandardToken.sol ERC20协议的标准实现
You should inherit from StandardToken or, for a token like you would want to
deploy in something like Mist, see HumanStandardToken.sol.
(This implements ONLY the standard functions and NOTHING else.
If you deploy this, you won't have anything useful.)
Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20.*/

pragma solidity ^0.4.8;
import "./Token.sol";
contract StandardToken is Token {
    function transfer(address _to, uint256 _value) returns (bool success) {
        //默认totalSupply 不会超过最大值 (2^256 - 1).
        //如果随着时间的推移将会有新的token生成,则可以用下面这句避免溢出的异常
        //require(balances[msg.sender] >= _value && balances[_to] + _value >balances[_to]);
        require(balances[msg.sender] >= _value);
        balances[msg.sender] -= _value;//从消息发送者账户中减去token数量_value
        balances[_to] += _value;//往接收账户增加token数量_value
        Transfer(msg.sender, _to, _value);//触发转币交易事件
        return true;
    }
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        //require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]);
        require(balances[_from] >= _value && allowed[_from][msg.sender] >=  _value);
        balances[_to] += _value;//接收账户增加token数量_value
        balances[_from] -= _value; ;//支出账户_from减去token数量_value
        allowed[_from][msg.sender] -= _value;//消息发送者可以从账户_from中转出的数量减少_value
        Transfer(_from, _to, _value);//触发转币交易事件
        return true;
    }
    //查询余额
    function balanceOf(address _owner) constant returns (uint256 balance) {
        return balances[_owner];
    }
    //授权账户_spender可以从消息发送者账户转出数量为_value的token
    function approve(address _spender, uint256 _value) returns (bool success)   
    {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }
    function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
      return allowed[_owner][_spender];//允许_spender从_owner中转出的token数
    }

    mapping (address => uint256) balances;
    mapping (address => mapping (address => uint256)) allowed;
}
  1. HumanStandardToken.sol 具体的token实现
This Token Contract implements the standard token functionality (https://github.com/ethereum/EIPs/issues/20) as well as the following OPTIONAL extras intended for use by humans.
In other words. This is intended for deployment in something like a Token Factory or Mist wallet, and then used by humans.
Imagine coins, currencies, shares, voting weight, etc.
Machine-based, rapid creation of many tokens would not necessarily need these extra features or will be minted in other manners.
1) Initial Finite Supply (upon creation one specifies how much is minted).
2) In the absence of a token registry: Optional Decimal, Symbol & Name.
3) Optional approveAndCall() functionality to notify a contract if an approval() has occurred.
.*/

import "./StandardToken.sol";
pragma solidity ^0.4.8;
contract HumanStandardToken is StandardToken {
    /* Public variables of the token */
    string public name;                   //名称: eg Simon Bucks
    uint8 public decimals;                //最多的小数位数How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether.
    string public symbol;                 //token简称: eg SBX
    string public version = 'H0.1';       //版本

    function HumanStandardToken(uint256 _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) {
        balances[msg.sender] = _initialAmount; // 初始token数量给予消息发送者
        totalSupply = _initialAmount;         // 设置初始总量
        name = _tokenName;                   // token名称
        decimals = _decimalUnits;           // 小数位数
        symbol = _tokenSymbol;             // token简称
    }
    /* 同意转出并调用接收合约(根据自己需求实现) */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this.
        //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
        //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
        
        require(_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData));
        return true;
    }
}

Token合约发布

我们将使用MetaMask( chrome插件安装链接https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?authuser=2)和Remix(https://ethereum.github.io/browser-solidity)并选择在Ropsten测试网络(获取免费ETH: http://www.jianshu.com/p/41580c29656f)进行Token合约发布。

  1. 切换MetaMask至Ropsten网络
image.png

2.将所有相关代码copy至Remix

image.png

在右侧上方红框标记处Environment选择Injected Web3, 下方的Account列表将会列出MetaMask中的账户信息。然后在右侧下方红框中依次填入初始化HumanStandardToken需要的参数,初始token数量(100000000)、token的名称描述(My Test Token)、decimal数(8)、token简称(MTT)。

  1. Token合约发布

点击合约HumanStandardToken处的create按钮,将弹出如下画面:

image.png

点击SUBMIT将该交易请求发布至Rosten网络中,交易变成功打包后。可以查询到本次操作的交易id为0x9fb4144f5a28ac46d43c689994de3cfa29cdb538582bde1ea1d646efcb7dcfa, 合约地址为0x06750c263b1a344b9b01b32888e93bc3c28a950d,交易截图如下:

image.png

值得一提的是, 本次交易刚好赶上Ropsten在区块1700000后进行BYZANTIUM( 拜占庭)分叉,遗憾的是交易最后被打包到了区块1700017,没有打包到1700001区块中。

image.png

4.合约源代码上传

接下来我们看下如何在https://ropsten.etherscan.io上传合约的源代码,首先打开合约0x06750c263b1a344b9b01b32888e93bc3c28a950d的详情页,并切换至Contrant Code标签中,如下图。

image.png

在Contract code 标签页中点击“Verify And Publish”链接,然后填入下图红框各项内容。

image.png
  • Contract address为上面创建的合约地址;
  • Contract name为在Remix中选择创建的合约名称HumanStandardToken;
  • Compiler版本需选择在Remix进行合约创建时选择的编译器版本一致;
  • 是否优化Optimization也需要与Remix发布合约时保持一致;
  • 在“Enter the Solidity Contract Code below”中填入之前在Remix中的solidity合约代码;
  • 在“Constructor Arguments ABI-encoded”中填入构造函数参数(100000000,“My Test Token”,8,“MTT”)的ABI编码,这个编码可以在创建合约的transaction的Input data中找出,如下图蓝色选中的部分即是构造函数参数的ABI编码(b0029之后所有的数据)。
image.png

填好上述数据后,点击Verify and Publish,如果验证通过了就会出现如下页面:

image.png

最后我们切换到合约的详情页,点击Contract Source标签,就能看到上传的合约源代码了,如下图:

image.png

如果遇到错误"Unable to Verify Contract at this point time",请确认在提交合约源代码时,选择的编译器版本与是否优化选项,与你在Remix中的选择是否一致。

  1. 添加发布的Token到钱包

我们可以在MetaMask中添加上面生成的Token至MetaMask钱包中,如下图。

image.png

点击Add后可以在Tokens标签页中看到上面发布的Token MTT. (因为我们在创建HumanStandardToken时,输入的初始量为100000000,而decimal值为8,这个初始量值的单位是token的最低单位,故总量为1MTT,大家可以根据自己的情况作出相应调整。)

image.png

接下来就可以在Ropsten测试网络上进行Token的转账以及相关的测试操作了,如果测试没问题就按照同样的流程把合约部署至以太坊主网络中发行真实的token了。

Good Luck!!!

推荐阅读更多精彩内容