A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小蜀哥哥 于 2018-6-8 09:55 编辑

基于以太坊私链代币发布
一、前言1.1 预备知识
   (1) 以太坊相关概念
   (2) 熟悉智能合约代码编写与部署
   (3) 以太坊私链环境搭建
   (4) Mist钱包的安装与使用
1.2 环境介绍
   (1) win10
   (2) Mist钱包
   (3) geth ---(version:1.7.2) ---搭建以太坊私链
二、以太坊代币(Token)
2.1 介绍

   如果不那么追求精确的定义,代币就是数字货币,比特币、以太币都可以定义代币。而以太坊代币是基于以太坊智能合约编写出来并发行到以太坊虚拟机上的合约数字货币,代币可以代表任何可以交易的东西,如:积分、财产、证书等等。利用以太坊智能合约能够非常轻松实现自己的代币,因为以太坊提供了一个开发代币的ERC20标准。
2.2 标准代币ERC20 Token

   ERC20和代币经常一同出现, ERC20是以太坊定义的一个代币标准。要求我们在实现代币的时必须要遵守的协议,如指定代币名称、总量、实现代币交易函数等,只有支持了协议才能被以太坊钱包支持,这样你的代币才具有交易和流通的能力。目前ERC20存在一些无法解决的问题,但是新的标准ERC223以及出世,但市面上大多数以太坊代币都采用ERC20标准,所以本文也使用ERC20标准实现自己的代码,最后会对ERC20存在的问题给大家稍作解释,那接下来我们就进入正题。
三、代币合约
3.1 ERC20的标准接口
contract ERC20 {
  function name() constant returns (string name)
  function symbol() constant returns (string symbol)
  function decimals() constant returns (uint8 decimals)
  function totalSupply() constant returns (uint totalSupply);
  function balanceOf(address _owner) constant returns (uint balance);
  function transfer(address _to, uint _value) returns (bool success);
  function transferFrom(address _from, address _to, uint _value) returns (bool success);
  function approve(address _spender, uint _value) returns (bool success);
  function allowance(address _owner, address _spender) constant returns (uint remaining);
  event Transfer(address indexed _from, address indexed _to, uint _value);
  event Approval(address indexed _owner, address indexed _spender, uint _value);
}

接口代码解释:
name()
   返回ERC20代币的名字,例如”My test token”。
symbol()
   返回代币的简称,例如:MTT,这个也是我们一般在代币交易所看到的名字。
decimals()
   返回token使用的小数点后几位。比如如果设置为3,就是支持0.001表示。
totalSupply()
   返回token的总供应量
balanceOf()
   返回某个地址(账户)的账户余额
transfer()
   从代币合约的调用者地址上转移value的数量token到的地址to,并且必须触发Transfer事件。transferFrom()
transferFrom方法用于允许合同代理某人转移token。前提是被代理人调用approve方法允许代理人设置操作自己多少token
allowance()
   被代理人设置代理人操作自己的多少token
approve()
   更改被代理人设置代理人操作自己的多少token,并且必须触发Approval事件
Transfer事件
   代币被转移时触发该事件,记录转账日志
Approval事件
   调用approve方法时触发,记录授权日志
   注意:以上ERC20标准代币接口方法只是标准,并不是所有方法都需要实现,当然我们还可以根据自己的业务增强自己的代币,比如实现代币管理、代币增发、代币兑换、资产冻结、Gas自动补充等功能的高级代币
3.2 代币合约实现
pragma solidity ^0.4.16;

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

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

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

四、部署
采用以太坊钱包mist+geth私有环境部署
   以太坊私链的搭建和mist钱包的安装与使用请自行学习
4.1 启动私链

4.2 新建账号

4.3 挖矿
挖矿:挖矿可以获得私有链的以太币,因为我们需要部署我们上面写的代币合约就需要消费gas,gas就是相应的以太币。挖矿默认采用第一个账号进行,如下所示
在控制台输入:miner.start() 即可开始挖矿

查看余额

4.4 打开Mist钱包并连接到私链

界面介绍

4.5 合约部署点击【CONTRACTS】进入合约部署界面


点击【DEPLOY NEW CONTRACT】开始部署新的合约  


点击最下面的【DEPLOY】弹出如下窗口(一定要在geth控制台执行miner.start(1)开启挖矿)


点击【WALLETS】进入回到主界面

五、测试Token点击【Main account】进入该账号界面


点击【Transfer Ether & Tokens】进入转账界面

在最下面点击【send】并在弹出框输入密码,待挖矿成功之后如下界面



当然你还可以在token合约主页面进行相关方法的测试,方法的测试有读者自行完成!
六、ERC20标准代币的问题

   ERC20有两种转账方式,一种是收件方为一份合同,这种情况下用户必须使用approve+transferFrom的功能来进行代币转移;而另一种则是收件方为合同外账户(例如,钱包地址)的情况,用户需将代币通过transfer功能转出 。如果用户使用transfer给合同地址转账将导致代币丢失,据了解,以太坊生态中的Golem代币,至今仍有93644.51美元的代币因投资者的无意操作而流失到合同地址内,造成这些代币的永久性丢失。因此ERC223就此诞生,相关介绍请读者自行学习!
七、结尾

   本文介绍了基于以太坊私链开发ERC20标准代币合约,并且使用Mist钱包部署,以及简单的转账测试。区块链技术是目前非常热门的,区块链技术暂时还不成熟,但是相信未来区块链在互联网的地位一定不可小觑,希望本文能够对读者有帮助。相互学习相互探讨不断专研

4 个回复

倒序浏览
我们是不是可以发一个成都校区的代币了,牛逼呀
回复 使用道具 举报
没沙发了!!!!
回复 使用道具 举报
66666!精品!
回复 使用道具 举报
期待成都校区专用币
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马