1
注:一般大家都普遍喜欢把 Token 叫成 代币,但是这样讲很不准确,如果非要叫成中文,我更认同 通证 这种理解。但为了保持原汁原味,不必非得翻译过来叫,所以本文统一保持英文的形式。

一般 Token 制作的门槛其实没有多高,所以大家完全都有能力可以去制作出自己命名的 Token。

不过你也别指望啥也不学就能做出一些拥有额外逻辑的 Token,比如众筹合约等,所以想要做的与众不同,那么就需要花些成本。

下面就直奔主题,我们不需要了解如何编写智能合约,因为以太坊提供的 ERC20 协议已经足够傻瓜式了,做一个简单的 Token 官方就有一个标准的智能合约代码作为示例,那么我们只需要知道制作的流程就行了。

啰嗦一句,其实学习其他的技术知识也是如此,我们不要急于追求很内在的东西,我们需要 正反馈 式学习法,先做出一些简单的 Demo,逐步在建立自信心和激发热情的同时不断深入学习。

安装 MetaMask

我们首先需要安装一个浏览器的插件 MetaMask,该插件作者非常 nice,主流的三大浏览器(Google、FireFox、Opera)上都可以安装这款实用的插件。安装这款插件的过程就不赘述了,现在假定我们已经装好了,点开该插件,经过两次 Accept 同意条款的操作后,进入创建账户的页面。

注意图上的三处箭头

  • 第一处箭头表示我们目前是处于 Ropsten Test Net 测试网络,与平常大家在用的以太坊主网络的主要区别就仅仅是大家只承认主网络上的以太币价值,而该测试网络主要用来进行开发测试。
  • 第二处箭头表示新建一个账户合约,作为以太坊网络上的一个合法用户。
  • 第三处箭头表示导入一个账户合约,假如你在该网络上有账户合约的话。

新建合约账户

在创建账户页面上输入自己的大于八位字符的密码后,进入 助记词 页面,这些助记词是唯一能帮助你保存自己账户合约的密钥,妥善保管。

购买以太币

不要慌,不是让你真的花钱去买,只需要在 MetaMask 提供的网站上点几下按钮就能获取到以太币(毕竟这些测试网络上的以太币没有实际价值)。

保存完助记词后我们就会进入到账户主页面,

我们可以看到我们的账户上余额是零,点击箭头1的购买按钮,进入确认页面

再点击上图中的箭头1按钮,进入购买页面,网址是 Test Ether Faucet

点击箭头1按钮,既可以获取到一个以太币(按一次获取一个),我获取了6个以太币,箭头2处即是转账交易记录。

OK,我们先暂时放一放 MetaMask,接下来让我们去玩玩 Remix

认识 Remix IDE

Remix 是一个基于浏览器的 Solidity IDE,基本的智能合约代码的开发调试在这上面进行是相当舒服的。Remix - Solidity IDE

  • 第一处箭头是项目目录栏,可以在这里创建文件目录
  • 第二处箭头是代码栏,我们的合约代码就在这里编写
  • 第三处箭头是日志栏,部署调试的日志输出都在这里显示
  • 第四处箭头是调试栏,对合约的部署、调试等操作都在这里进行

需要熟悉这个工具吗?当然,而且越熟悉越好,但不是现在,别忘了我们现在的目标是制作自己的 Token。

Token 合约

以太坊官方网站上有一份标准的 Token 合约供学习者参考,而且也有很详细的教程教大家如何去部署自己的 Token。Create a cryptocurrency contract in Ethereum

本文与官方不同的是我们使用 轻钱包 MetaMask 和在线开发环境 Remix 进行开发调试部署,官方的以太坊钱包对于初学者来说有个挺苦恼的地方,同步主网络或者测试网络的全节点数据很慢,而且占用磁盘空间较大。初次体验尽量还是选择轻松的方式,不要给自己留下坏心情 :)

我们将官方的 Token 合约代码拷贝到 Remix 的代码栏中,合约代码这里也贴一下:

pragma solidity ^0.4.16;

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

contract TokenERC20 {
    // Public variables of the token
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    // 18 decimals is the strongly suggested default, avoid changing it
    uint256 public totalSupply;

    // This creates an array with all balances
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    // This generates a public event on the blockchain that will notify clients
    event Transfer(address indexed from, address indexed to, uint256 value);

    // This notifies clients about the amount burnt
    event Burn(address indexed from, uint256 value);

    /**
     * Constructor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    function TokenERC20(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
    }

    /**
     * Internal transfer, only can be called by this contract
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // Prevent transfer to 0x0 address. Use burn() instead
        require(_to != 0x0);
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to] + _value > balanceOf[_to]);
        // Save this for an assertion in the future
        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);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
     * Transfer tokens
     *
     * Send `_value` tokens to `_to` from your account
     *
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    /**
     * Transfer tokens from other address
     *
     * Send `_value` tokens to `_to` on behalf of `_from`
     *
     * @param _from The address of the sender
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    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;
    }

    /**
     * Set allowance for other address
     *
     * Allows `_spender` to spend no more than `_value` tokens on your behalf
     *
     * @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;
    }

    /**
     * Set allowance for other address and notify
     *
     * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     * @param _extraData some extra information to send to the approved contract
     */
    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;
        }
    }

    /**
     * Destroy tokens
     *
     * Remove `_value` tokens from the system irreversibly
     *
     * @param _value the amount of money to burn
     */
    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;
    }

    /**
     * Destroy tokens from other account
     *
     * 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;
    }
}

部署合约

接下来我们先选择好我们的编译器的版本,对应合约代码首行的版本号 pragma solidity ^0.4.16

点击箭头1的标签页,进入设置界面,然后点击箭头2选择相应的编译器版本,这里即选择 0.4.16。

然后点击编译标签(Compile),会看到已经编译成功

然后点击运行标签(Run),进行部署操作

  • 第一处箭头选择部署环境,这边会自动检测到我们的 MetaMask 当前所处的网络(Ropsten Test Net)。
  • 第二处箭头输入我们 Token 的一些基本信息

    • 第一个参数表示 Token 总发行量,我这里随便填了一千万个
    • 第二个参数表示 Token 名称
    • 第三个参数表示 Token 符号,一般在流通过程中均以该符号作为 Token 的标识
  • 第三处箭头点击即创建部署交易单,开始最终的部署

这里唯一可以改的是 Gas Limit 和 Gas Price,一般来讲这两个数据会自动根据当前合约的复杂度和市场平均水准生成。所以一般不需要去改这两个值,如果是囊中羞涩,改低一点也是可以的,但是可能会出现 Gas 被消耗完的情况(Gas 一旦被消耗完而合约仍未部署到网络上,那么所有操作都会进行回滚,除了你支付的矿工费)。

点击发布按钮(SUBMIT),静静等待发布成功吧!

此时打开 MetaMask 页面

上图为发布中状态,下图为发布结束的状态,此时会发现我们的账户余额减少了一些,因为被用于支付发布合约的矿工费了。

到这里还不清楚是否成功发布了,点击交易单进入交易详情页面,当你看到箭头1处的 Success 时就表示我们的 Token 发布成功啦!

点击箭头2处的合约地址链接,进入合约详情页

点击箭头处的 Token 地址链接,进入 Token 详情页

至此我们的 Token 就做完啦!

钱包中显示 Token

我们以 MetaMask 为例演示一下,其他钱包比如 ImToken 同样的只需要将 Token 地址复制过去就能看到了。

先切换到 Token 标签页

点击添加 Token,输入刚才的 Token 合约地址

点击添加按钮,就能在 MetaMask 上看到我们自己的 Token 了。

小结

在制作 Token 的整个过程中,并没有难点,只是大多数人不清楚流程,当自己实操一遍后立马就能上手。后面我们会去玩一些复杂一点的智能合约,但同样的,需要一些基本的 Solidity 编程语言的知识以及区块链相关的知识。Solidity 这门以太坊的标准编程语言之后的文章也会详细讲解,大家尽请期待~

欢迎关注公众号:『比特扣』,与我一起探索区块链的世界。

封不羁
1.1k 声望82 粉丝