开发者社区> citibank> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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

简介: 本文将介绍基于以太坊测试链,利用Remix与MetaMask两个工具发行Token(代币),以及基于ERC20标准规范编写代币合约,供初学者参考。 ERC20 Token也许你经常看到ERC20和代币一同出现, ERC20是以太坊定义的一个代币标准。
+关注继续查看

本文将介绍基于以太坊测试链,利用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的,只不过可以免费获得,为了发行合约,我们需要测试链账户,下面我们将插入使用MetaMaskchrome插件来链接创建账户:

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
yum install mysql-community-server 出现类似报错:Requires: libtirpc.so.3()(64bit)
原因:Linux 版本与下载的 Mysql 的yum 库版本不兼容,例如 Centos7 安装 el8 版本的 Mysql;
244 0
让你的QuickBI报表快起来: 使用DLA Presto来无缝加速
QuickBI提供了通过DLA Presto加速MaxCompute查询的功能,在TPCH测试中,查询的性能提升最小的1倍多,最大的8倍多,让您的报表飞起来 :)
978 0
java B2B2C 源码 多级分销Springcloud多租户电子商城系统-KAFKA介绍
Kafka是一个分布式的、可分区的、可复制的消息系统。 它提供了普通消息系统的功能,但具有自己独特的设计。这个独特的设计是什么样的呢? 首先让我们看几个基本的消息系统术语: Kafka将消息以topic为单位进行归纳。
1034 0
java B2B2C 源码 springmvc mybatis多租户电子商城系统- Stream重新入队(RabbitMQ)
本文将介绍RabbitMQ的binder提供的重试功能:重新入队 准备一个会消费失败的例子,可以直接沿用前文的工程,也可以新建一个,然后创建如下代码的逻辑: @EnableBinding(TestApplication.
1244 0
JAVA ssm b2b2c多用户商城系统源码-hystrix工作原理
Hystrix是Netflix开源的一个限流熔断的项目、主要有以下功能:隔离(线程池隔离和信号量隔离):限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。优雅的降级机制:超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
1388 0
如何创建以太坊ERC20代币
看这篇文章需要对以太坊,智能合约,代币等概念有基本的了解。 什么是ERC20 可以把ERC20简单理解成以太坊上的一个代币协议,所有基于以太坊开发的代币合约都遵守这个协议。
1556 0
解决yum安装mysql时Requires: libc.so.6(GLIBC_2.17)(64bit)
1、yum install mysql-community-server 1 2 3 4 5 6 7 Error: Package: mysql-community-libs-5.7.17-1.
5105 0
dTree无限级目录树和JQuery同步ajax请求
以前都是用JQuery对树的支持来实现目录树的,近来闲来无事就弄了下dTree,感觉其无限级目录还是挺好的,而且它的使用也比较方便,基本上就是先把要用的js文件即dtree.js和css文件dtree.
768 0
基于SQL Server 2008 Service B“.NET研究”roker构建企业级消息系统
  1、引言   Microsoft 在SQL Server 2005引入了服务代理 (Service Broker 简称SSB) 为技术支持代理设计模式和面向消息的中间件 (MOM) 的原则。Service Broker在SQL Server 2008上得到完善, SQL Server Service Broker 为消息和队列应用程序提供 SQL Server 数据库引擎本机支持。
1008 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
比特币交易所安全内幕
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载