以太坊ERC20协议以及发行自己代币

什么是 ERC20

ERC-20 标准是在2015年11月份推出的,使用这种规则的代币,表现出一种通用的和可预测的方式。

简单地说,任何 ERC-20 代币都能立即兼容以太坊钱包(几乎所有支持以太币的钱包,包括Jaxx、MEW、imToken等,也支持 erc-20的代币),由于交易所已经知道这些代币是如何操作的,它们可以很容易地整合这些代币。这就意味着,在很多情况下,这些代币都是可以立即进行交易的。

标准化非常有利,也就意味着这些资产可以用于不同的平台和项目,否则只能用在特定的场合。

代币(Token)是区块链中定义价值的方式,用于标定金融或数字资产。在以太坊上,代币使用相同的标准,这样代币之间的兑换和DAPP支持就会变得容易。

标准规定了哪些内容

ERC20 是各个代币的标准接口。ERC20 代币仅仅是以太坊代币的子集。为了充分兼容 ERC20,开发者需要将一组特定的函数(接口)集成到他们的智能合约中,以便在高层面能够执行以下操作:

  • 获得代币总供应量
  • 获得账户余额
  • 转让代币
  • 批准花费代币

ERC20 让以太坊区块链上的其他智能合约和去中心化应用之间无缝交互。一些具有部分但非所有ERC20标准功能的代币被认为是部分 ERC20兼容,这还要视其具体缺失的功能而定,但总体是它们仍然很容易与外部交互。

ERC20 标准

ERC20 标准定义了一个兼容协议, 需要实现的函数. 具体如下.

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

同时规定了三个必须定义的变量,分别是

  • 合约名称
  • 合约代号
  • 合约进制
     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

ERC20 并不是完美的

ERC-20标准还有待完善。

  1. 其中一个障碍是,将令牌直接发送给令牌的智能合同将导致资金损失。这是因为一个令牌的合同只会跟踪和分配资金。例如,当您从钱包中向另一个用户发送令牌时,该钱包将调用令牌的合约来更新数据库。所以如果您试图将令牌直接传输到令牌的合约中,那么由于该令牌的合约无法响应,所以金钱就“丢失”了。
  2. ERC20标准无法通过接收方合同处理传入的交易。这是该令牌存在的最大问题,也是开发者一直希望改进的地方。ERC20令牌无法将令牌发送给一个与这些令牌不兼容的契约,也正因为这样,部分资金存在丢失的风险。
  3. Reddit上的一篇文章指出,由于被发送到“错误”的合同上,大约价值40万美元的ERC20令牌被困,这对整个以太坊生态系统而言是一个巨大的威胁。幸运的是,ERC223令牌可以解决这一难题,前提是该令牌能够获得批准并被引入。

抽象

以下标准允许在智能合约中实施标记的标记API。 该标准提供了转移token的基本功能,并允许token被批准,以便他们可以由另一个在线第三方使用。

动机

标准接口可以让Ethereum上的任何令牌被其他应用程序重新使用:从钱包到分散式交换。

规则

Token

方法

注意:调用者必须处理返回false的returns (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)

发行代币demo

代码

pragma solidity ^0.4.20;

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

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

    /**
     * 初始化构造
     */
    constructor(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;
        emit 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
        emit 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
        emit Burn(_from, _value);
        return true;
    }
}

在remix中编译运行上面的上面的智能合约,键入参数,配合metamask插件,

在METAMASK中查看自己的代币:

ERC223要解决的首要问题是什么?

自从引入ERC20令牌标准以来,几乎所有的基于以太坊的令牌都成功的接受了这个新标准。然而其自身的缺点需要及时解决,这便是ERC223令牌诞生的原因。

防止丢失

ERC223令牌标准将向现有的ERC20标准引入一个新功能,以防止意外转移的发生。ERC223令牌标准可以防止令牌在以太坊网络上丢失。

困难的转换

假设 ERC223 令牌标准能够取代ERC20成为新的标准,现有令牌的发行方需要做一些艰难的决定。因为,从现实情况来看,不管用何种方式,从ERC20转换到ERC223是不可能的,同样的,所有ERC20令牌都需要在ERC223标准下重新部署。这也意味着任何交易平台的上市都需要更新他们的信息和地址。这是一个艰苦的过程,这也就意味着,在未来很少有现有的令牌被有效地转换为ERC223。

正如Alex van de Sande在Reddit上指出的那样,“更方便”的过程可能是创建新的令牌,它们是通过持有旧令牌的合同支持的。这可能是大多数项目最合理的选择,但只有时间才能确定哪些选项将被实施。

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏区块链入门

【链安】智能合约DoS攻击原理分析及相应漏洞修复

DoS 是DenialOfService,拒绝服务的缩写[3],从字面上来理解,就是用户所需要的服务请求无法被系统处理。 打个比方来形容DoS,火车站是为大家...

1204
来自专栏区块链入门

第二十一课 如何通过Solidity的智能合约函数把长文章记录到以太坊区块链上?

辉哥的文章《第十六课 不用编程,如何把长文章记录到以太坊区块链上?》 是通过MetaMask的交易形式把数据写到链上。 本文提供另外一个方式,通过智能合约函数...

952
来自专栏区块链入门

第三课 以太坊术语说明及开发者资源列表

也称钱包,提供账户管理、挖矿、转账、智能合约的部署和执行等等功能,以太坊节点利用以太坊客户端接入到以太坊网络。 现在以太坊客户端主要有:Wallent/ist ...

852
来自专栏区块链大本营

以太坊再爆高危漏洞!黑客增发ATN 1100万枚token事件始末

事情发生在5月中旬,ATN技术人员发现Token合约由于存在漏洞受到攻击。不过ATN基金会随后透露,将销毁1100万个ATN,并恢复ATN总量,同时将在主链上线...

531
来自专栏蜉蝣禅修之道

以太坊DApp系列(二)---从入门到出家

以太坊自2013年V神提出后,被无数人赋予美好的愿景,甚至被称为区块链2.0,其代币发行量更是达到了全球第二,仅次于比特币,而其带来的智能合约概念颠覆了人们对区...

1.2K18
来自专栏深入浅出区块链技术

实现一个可管理、增发、兑换、冻结等高级功能的代币

2554
来自专栏FreeBuf

浅析AMR智能合约批量转账溢出漏洞

日前,互联网爆出AMR合约存在高危安全风险的交易,该合约存在批量转账溢出漏洞,当合约实现批量转账功能时,容易在计算通证增加量时发生溢出漏洞,BUGX.IO安全团...

1193
来自专栏区块链入门

【 链安科技】constructor函数使用漏洞

2018年7月12日,成都链安科技(LianAn Technology)智能合约审计小组使用自主研发的VaaS平台对以太坊链上智能合约进行安全审计的过程中,发现...

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

SAP最佳业务实践:FI–资产会计(162)-5 ABAVN 资产处置

4.4 资产处置 固定资产清理是指从资产组合中移除某项资产或部分资产。复杂固定资产(或部分复杂固定资产)的移除是从帐面上将其作为资产清理过帐。 在中国资产会计中...

4088
来自专栏区块链入门

【区块链安全】技术小白如何做到让一行代码值64亿元?

2018年4月24日,又一件突发性事件引爆了币圈!刚刚发行了才两个月的“美链 Beauty Chain” (简称BEC)在受到黑客的攻击的影响下直接归零了!黑客...

1204

扫码关注云+社区

领取腾讯云代金券