第七课 技术小白如何在45分钟内发行通证(TOKEN)并上线交易

1. 文章摘要
【本文目标】

通过逐步的指导和截图举证,一步步带领一个技术小白完成一个数字货币(通证,代币,TOKEN)的发布演示和上线交易。

【环境前置条件】

参考《第六课 技术小白如何开发一个DAPP区块链应用(以宠物商店为例)》,已在本地WIDOWS环境完成MetaMask轻钱包客户端的安装和配置;作者建议最好遵循从头开始的课程学习顺序。不过如果你想半途插入实操学习,问题也不大,遇到障碍时反向找对应文章的指导内容即可完成。

【技术收获】

从本实践中,你可以学习到: ERC20 Token的定义和实践 使用Remix Solidity IDE编写智能合约和编译调试 使用MetaMask完成钱包账户查看

2. ERC20 Token定义和接口说明

定义

ERC20合约是在2015年11月在EIP上提出的一个合约标准,代币定义的一个标准。 Token代表数字资产,具有价值,但是并不是都符合特定的规范。 基于ERC20的货币更容易互换,并且能够在Dapps上相同的工作。 新的标准可以让token更兼容,允许其他功能,包括投票标记化。操作更像一个投票操作,Token的持有人可以完全控制资产,遵守ERC20的token可以跟踪任何人在任何时间拥有多少token。基于eth合约的子货币,所以容易实施。

ERC20 Token接口说明

方法

注意:调用者必须处理返回falsereturns (bool success).调用者绝对不能假设返回false的情况不存在。

name

返回这个令牌的名字,比如"MyToken". 可选 - 这种方法可以用来提高可用性,但接口和其他契约不能指望这些值存在。

function name() constant returns (string name)

symbol

返回令牌的符号,比如HIX. 可选 - 这种方法可以用来提高可用性,但接口和其他契约不能指望这些值存在。

function symbol() constant returns (string symbol)

decimals

返回token使用的小数点后几位, 比如 8,表示分配token数量为100000000

可选 - 这种方法可以用来提高可用性,但接口和其他契约不能指望这些值存在。

function decimals() constant returns (uint8 decimals)

totalSupply

返回token的总供应量。

function totalSupply() constant returns (uint256 totalSupply)

balanceOf

返回地址是_owner的账户的账户余额。

function balanceOf(address _owner) constant returns (uint256 balance)

transfer

转移_value的token数量到的地址_to,并且必须触发Transfer事件。 如果_from帐户余额没有足够的令牌来支出,该函数应该被throw

创建新令牌的令牌合同应该在创建令牌时将_from地址设置为0x0触发传输事件。

注意 0值的传输必须被视为正常传输并触发传输事件。

function transfer(address _to, uint256 _value) returns (bool success)

transferFrom

从地址_from发送数量为_value的token到地址_to,必须触发Transfer事件。

transferFrom方法用于提取工作流,允许合同代您转移token。这可以用于例如允许合约代您转让代币和/或以子货币收取费用。除了_from帐户已经通过某种机制故意地授权消息的发送者之外,该函数应该throw。

注意 0值的传输必须被视为正常传输并触发传输事件。

function transferFrom(address _from, address _to, uint256 _value) returns (bool success)

approve

允许_spender多次取回您的帐户,最高达_value金额。 如果再次调用此函数,它将以_value覆盖当前的余量。

注意:为了阻止向量攻击,客户端需要确认以这样的方式创建用户接口,即将它们设置为0,然后将其设置为同一个花费者的另一个值。虽然合同本身不应该强制执行,允许向后兼容以前部署的合同兼容性

function approve(address _spender, uint256 _value) returns (bool success)

allowance

返回_spender仍然被允许从_owner提取的金额。

function allowance(address _owner, address _spender) constant returns (uint256 remaining)

Events

Transfer

当token被转移(包括0值),必须被触发。

event Transfer(address indexed _from, address indexed _to, uint256 _value)

Approval

当任何成功调用approve(address _spender, uint256 _value)后,必须被触发。

event Approval(address indexed _owner, address indexed _spender, uint256 _value)

[官网接口说明点击查看],(https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) 接口文件ERC20Interface.sol如下:

contract ERC20Interface {

    string public constant name = "Token Name";
    string public constant symbol = "SYM";
    uint8 public constant decimals = 18;  // 18 is the most common number of decimal places
    // 0.0000000000000000001  个代币

    function totalSupply() public constant returns (uint);

    function balanceOf(address tokenOwner) public constant returns (uint balance);

    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    function approve(address spender, uint tokens) public returns (bool success);

    function transfer(address to, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);


    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

3. TOKEN合约代码

合约文件TokenERC20.sol如下:

pragma solidity ^0.4.16;

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

函数的功能参考函数的说明描述和代码自解释。

4. 合约编译部署和发布

MetaMask钱包联网

【前置条件】作者假设学习者已完成MetaMask的安装和配置。还没有完成的,参考《第六课 技术小白如何开发一个DAPP区块链应用(以宠物商店为例)》的章节“5. 安装 MetaMask和配置区块链网络”,在本地WIDOWS环境完成MetaMask轻钱包客户端的安装和配置。

打开MetaMask钱包,点击左上角的“Ropsten Test Network” 连接成功。

连接成功

查看存量的账号Account 1,其中ETH余额显示为0。

存量账号Account 1余额为0

点击“Copy Address to Clipboard”,记录Account 1钱包地址为

0xD1F7922e8b78cBEB182250753ade8379d1E09949

点击MetaMask右上角环形头像的菜单“Create Account”, 创建一个新钱包账号Account 8,

新增账号成功

记录Account 8的钱包地址为

0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363

点击Account 8的"BUY"按钮,可以从“Ropsten Test Network” 免费获取一些测试ETH。

跳转到BUY ETH页面

点击按钮“ROPSTEN TEST FAUCET”可以看到赠送页面

点击绿色按钮,等待10秒以上,成功的话每次可以获取1个测试ETH。 【故障排查】

赠送失败

作者操作时,出现错误。从提示看,是由于用户交易拒绝。等10秒后再点击该绿色按钮则未有错误提示了。原因不明,可能是操作频繁导致。 多次点击,偶尔出错,小编一共从这个测试网站获取了5个测试ETH用于作为发币的GAS燃料。

获取测试ETH

Remix Solidity IDE调试环境介绍

1,代码编写和编译 我们以第二课的“Hello World”智能合约为例,参考下图可完成编译和语法错误发现。

编译环境使用方法

2,合约创建 参考附图描述,在配置号MetaMask账号和网络连接的情况下,确保账户有虚拟ETH用于合约创建花销。最后点击“Create”按钮可以完成合约创建。点击输出区的网页链接可以查看合约的详细情况。

支付合约花销

image.png

3, 合约执行 参考附件路径图,点击RUN按钮可以执行一次合约。点击下方的“Say”按钮,可以看到合约输出。在调试输出窗口可以看到“call to hello.say”的运行提示。

合约执行

【总结】所以说,没有Ubuntu+Ganache等,直接在WINDOWS环境,也可以使用Remix+MetaMask+Ropsten Test Network组合完成一套完整的以太坊测试环境。 更详细的REMIX帮助文档参考第十课 Solidity语言编辑器REMIX指导大全

编译ERC20智能合约

CHROME浏览器打开Remix Solidity IDE环境,打开之前编写的“TokenERC20.sol”智能合约,然后点击右侧的“start to compile”按钮。可以看到,除了一些Warning提示外,智能合约编译成功。

编译操作

运行ERC20智能合约

运行页面

切换到"RUN"页面,Environment选择“Injected Web3”, Account自动更新为MetaMask的Account 8账号。“Create”按钮前按照发币数量(本次发1个亿),代币名称,代币符号填写为100000000,"ColorBay","CB"。 点击"Create"按钮,会弹出一个交易确认框,设置合理的Gas Price,但要确保Max Transaction Fee不会超过Account 8的ETH总数,点“SUBMIT”按钮。

智能合约部署

部署成功的话,Account8 会产生一条交易记录,显示状态为“Contract Published”,表示部署成功了。

部署成功

【说明】有时点击账号,会出现“Retry with a higher gas price here”的提示,一般情况下再等待10秒看看能否交易成功。万一还是不成的话,可考虑该Gas Price大点。

部署确认中

MetaMask加载TOKEN

点击Account 8的交易记录,可以跳转智能合约部署信息显示页面:

部署成功

获取智能合约地址为0x5eeec41dc08d7caece17c4a349635934637036f1,点击可查看该交易详情。

点击MetaMask的Account 8账号的TOKENS页面的“ADD TOKEN”按钮,把代币智能合约地址Token Contract Address为0x5eeec41dc08d7caece17c4a349635934637036f1,代币标识符Token Symbol为CB,则可以创建你的代币了。

代币创建成功,一共有1亿个CB币了。此时,点击100000000的代币位置,跳转到代币查询页面, 可以在etherscan网站看到账户的持币数量了。

1亿

TOKEN信息

截止作者发稿时,以太坊的价格为2559元/个,而你有1亿个CB币,是不是快要走上人生巅峰了呢?哈哈,做个梦而已。真正的区块链应用,要能有生态,能为人类社会创造价值,而不是讲故事,割韭菜!

5. 代币交易

由于MetaMask插件没有提供代币交易功能,同时考虑到很多人并没有以太坊钱包或是被以太坊钱包网络同步问题折磨,今天我用网页钱包来讲解代币交易。 1. 进入网页钱包地址 第一次进入有一些安全提示需要用户确认。 2. 进入之后,按照下图进行设置

网络配置

3,增加自定义代币 填写地址Token Contract 为0x5eeec41dc08d7caece17c4a349635934637036f1和其他信息,点击保存按钮。

增加自定义TOKEN

配置TOKEN合约地址和代币符号

配置成功的界面如下:

【说明】如果不成功,则提示为“Not a valid ERC-20 token CB”,则可能是代币信息填写不正确或者右上角的网络选择错误,没有选择“Network Ropston(infura.com)”选项引起。

4.转账给Account 1 填写Account 1的地址“0xD1F7922e8b78cBEB182250753ade8379d1E09949”到“发送至地址”输入框,

选择CB,不是ETH

转账确认

交易确认

交易提示

查看账户余额,Account 8减少800万个CB币,而Account 1则增加了800万个CB币。

Account 8

Account 1

转账交易成功了! 作为一个古典投资人,用45分钟完成了TOKEN上线和交易,用4个小时整理了这篇文章。学习就是这么简单,只要你对新技术保持饥饿感,高龄开发转型区块链也不是难事!

6. 总结参考文档

本文是站在巨人的肩膀上的操作实践,感谢以下文章作者的帮助:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

SSH僵尸主机挖矿木马预警

XMR(门罗币)是目前比特币等电子货币的一种,以其匿名性,支持CPU挖矿,以及不菲的价格等特点,得到了“黑产”的青睐。瀚思科技挖掘出黑产利用互联网服务器进行挖矿...

3119
来自专栏SAP最佳业务实践

SAP最佳业务实践:FI–资产会计(162)-2资产主数据

4 流程步骤 4.1 主数据维护 固定资产是企业拥有的对象、权限或其他项目,旨在长期使用并可在资产负债表中个别标识。维护固定资产包含创建、更改和显示资产主记录...

3556
来自专栏岑玉海

RChain的跨分片交易算法

1922
来自专栏程序猿DD

如何在Spring Boot中玩转智能合约【修订版】

本文是由链博科技 ChainBoard.IO 为大家带来的web3j 对智能合约的调用。让 java 程序可以和我们的智能合约愉快的交互起来~ 一、 什么是 w...

38212
来自专栏极客编程

如何实现以太坊支付

在这篇文章中,我将实现一个简单但完整的以太坊支付通道。支付通道使用密码签名,以安全、即时、无交易费用重复地传送Ether。

3362
来自专栏区块链大本营

教程 | 只用5步,教你从零用Truffle打造第一个以太坊Dapp!

7706
来自专栏申龙斌的程序人生

同时给200多人发送比特币,程序员是这样做到的

在币圈中,使用自己的钱包给他人发币时,就跟平常的微信、支付宝扫码付款类似,输入BTC地址和金额,再输入支付密码,不管对方是否在地球的另一端,不需要任何银行的中介...

1692
来自专栏区块链大本营

千万别惹牛人!小哥被盗22元后,整出了这篇以太坊钱包安全攻略,黑客看完得哭了...

对于区块链动辄几十万行的代码量,安全漏洞时不时就冒出来。敏锐的黑客们,虎视眈眈地盯着漏洞的闸门,一旦看见开闸,便以迅雷不及掩耳的速度展开偷袭。

1222
来自专栏区块链技术指北

区块链钱包开发

文/温国兵 本文由币乎社区(bihu.com)内容支持计划奖励。 这是「区块链技术指北」的第 21 篇文章。 如果对我感兴趣,想和我交流,我的微信号:Wenta...

9548
来自专栏区块链入门

第十三课 如何在DAPP应用实现自带钱包转账功能?

区块链是一个伟大的发明,它改变了生产关系。很多生态,有了区块链技术,可以由全公司员工的"全员合伙人"变成了全平台的”全体合伙人”了,是真正的共享经济模式。

1726

扫码关注云+社区

领取腾讯云代金券