以太坊:什么是ERC20标准?

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

简单地说,任何 ERC-20 代币都能当即兼容以太坊钱包(几乎全部支持以太币的钱包,包括Jaxx、MEW、imToken等,也支持 erc-20的代币),因为交易所已经知道这些代币是如何操做的,它们能够很容易地整合这些代币。这就意味着,在不少状况下,这些代币都是能够当即进行交易的。github

标准规定了哪些内容数组

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

一、得到代币总供应量网络

二、得到帐户余额app

三、转让代币函数

四、批准花费代币区块链

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

ERC20 标准spa

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

 

// ----------------------------------------------------------------------------

  // ERC Token Standard #20 Interface

  // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md

  // ----------------------------------------------------------------------------

  contract ERC20Interface {

      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 transfer(address to, uint tokens) public returns (bool success);

     function approve(address spender, 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

标准化很是有利,也就意味着这些资产能够用于不一样的平台和项目,不然只能用在特定的场合。

token的接口标准

抽象

如下标准容许在智能合约中实施标记的标记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)

实施

在Ethereum网络上部署了大量符合ERC20标准的令牌。 具备不一样权衡的各类团队已经编写了不一样的实施方案:从节省gas到提升安全性。

网上的例子:

https://github.com/ConsenSys/Tokens/blob/master/contracts/StandardToken.sol

https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/StandardToken.sol

 

ERC20 Token标准接口

如下是一个接口合同,声明所需的功能和事件以符合ERC20标准:

// https://github.com/ethereum/EIPs/issues/20

  contract ERC20 {

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

    }

大部分Ethereum主要标记符合ERC20标准。

一些令牌包括描述令牌合同的进一步信息:

string public constant name = "Token Name";

string public constant symbol = "SYM";

uint8 public constant decimals = 18;  // 大部分都是18

 

如何工做?

如下是令牌合约的一个片断,用于演示令牌合约如何维护Ethereum账户的令牌余额

 

contract TokenContractFragment {

     // Balances 保存地址的余额

     mapping(address => uint256) balances;

     // 账户的全部者批准将金额转入另外一个账户

     mapping(address => mapping (address => uint256)) allowed;

 

      // 特定账户的余额是多少?

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

          return balances[_owner]; //从数组中取值

      }

 

      // 将余额从全部者账户转移到另外一个账户

      function transfer(address _to, uint256 _amount) returns (bool success) {

          //判断条件 发送者余额>=要发送的值  发送的值>0  接收者余额+发送的值>接收者的余额

          if (balances[msg.sender] >= _amount

              && _amount > 0

              && balances[_to] + _amount > balances[_to]) {

              balances[msg.sender] -= _amount;   //发送者的余额减小

              balances[_to] += _amount;         //接收者的余额增长

              return true;

         } else {

              return false;

          }

      }

 

      // 发送 _value 数量的token从地址 _from 到 地址 _to

      // transferFrom方法用于提取工做流程,容许合同以您的名义发送令牌,例如“存入”到合同地址和/或以子货币收取费用; 该命令应该失败,除非_from账户经过某种机制故意地受权消息的发送者; 咱们提出这些标准化的API来批准:

      function transferFrom(

          address _from,

          address _to,

          uint256 _amount

     ) returns (bool success) {

          //和上面同样的校验规则

          if (balances[_from] >= _amount

              && allowed[_from][msg.sender] >= _amount

              && _amount > 0

              && balances[_to] + _amount > balances[_to]) {

              balances[_from] -= _amount;

              allowed[_from][msg.sender] -= _amount; //减小发送者的批准量

              balances[_to] += _amount;

              return true;

         } else {

             return false;

          }

      }

 

      // 容许_spender屡次退出您的账户,直到_value金额。 若是再次调用此函数,它将以_value覆盖当前的余量。

      function approve(address _spender, uint256 _amount) returns (bool success) {

          allowed[msg.sender][_spender] = _amount; //覆盖当前余量

          return true;

      }

  }

 

 

token余额

假设token合约内有两个持有者

 

0x1111111111111111111111111111111111111111有100个单位

0x2222222222222222222222222222222222222222有200个单位

那么这个合约的balances结构就会存储下面的内容

 

balances[0x1111111111111111111111111111111111111111] = 100

balances[0x2222222222222222222222222222222222222222] = 200

 

 

 

那么,balanceOf(...)就会返回下面的结果

 

tokenContract.balanceOf(0x1111111111111111111111111111111111111111) 将会返回 100

tokenContract.balanceOf(0x2222222222222222222222222222222222222222) 将会返回 200

 

 

 

转移token的余额

若是0x1111111111111111111111111111111111111111想要转移10个单位给0x2222222222222222222222222222222222222222,那么0x1111111111111111111111111111111111111111会执行下面的函数

 

tokenContract.transfer(0x2222222222222222222222222222222222222222, 10)

 

 

token合约的transfer(...)方法将会改变balances结构中的信息

 

balances[0x1111111111111111111111111111111111111111] = 90

balances[0x2222222222222222222222222222222222222222] = 210

 

 

balanceOf(...)调用就会返回下面的信息

 

tokenContract.balanceOf(0x1111111111111111111111111111111111111111) 将会返回 90

tokenContract.balanceOf(0x2222222222222222222222222222222222222222) 将会返回 210

 

 

从token余额批准和转移

若是0x1111111111111111111111111111111111111111想要批准0x2222222222222222222222222222222222222222传输一些token到0x2222222222222222222222222222222222222222,那么0x1111111111111111111111111111111111111111会执行下面的函数

 

tokenContract.approve(0x2222222222222222222222222222222222222222, 30)

 

 

而后allowed(这里官方文档写的是approve,很明显是错的)结构就会存储下面的内容

 

tokenContract.allowed[0x1111111111111111111111111111111111111111][0x2222222222222222222222222222222222222222] = 30

 

 

若是0x2222222222222222222222222222222222222222想要晚点转移token从0x1111111111111111111111111111111111111111到他本身,0x2222222222222222222222222222222222222222将要执行transferFrom(...)函数

 

tokenContract.transferFrom(0x1111111111111111111111111111111111111111, 20)

 

 

balances的信息就会变成下面的

 

tokenContract.balances[0x1111111111111111111111111111111111111111] = 70

tokenContract.balances[0x2222222222222222222222222222222222222222] = 230

 

 

而后allowed就会变成下面的内容

 

tokenContract.allowed[0x1111111111111111111111111111111111111111][0x2222222222222222222222222222222222222222] = 10

 

 

0x2222222222222222222222222222222222222222仍然能够从0x1111111111111111111111111111111111111111转移10个单位。

 

tokenContract.balanceOf(0x1111111111111111111111111111111111111111) will return 70

tokenContract.balanceOf(0x2222222222222222222222222222222222222222) will return 230

 

简单修复的token合约

如下是一个令牌合同,固定供应量为1000000单位,最初分配给合同全部者:

 

pragma solidity ^0.4.8;

 

  // ----------------------------------------------------------------------------------------------

  // Sample fixed supply token contract

  // Enjoy. (c) BokkyPooBah 2017. The MIT Licence.

  // ----------------------------------------------------------------------------------------------

 

   // ERC Token Standard #20 Interface

  // https://github.com/ethereum/EIPs/issues/20

  contract ERC20Interface {

      // 获取总的支持量

      function totalSupply() constant returns (uint256 totalSupply);

 

      // 获取其余地址的余额

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

 

      // 向其余地址发送token

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

 

      // 从一个地址想另外一个地址发送余额

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

 

      //容许_spender从你的帐户转出_value的余额,调用屡次会覆盖可用量。某些DEX功能须要此功能

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

 

      // 返回_spender仍然容许从_owner退出的余额数量

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

 

      // token转移完成后出发

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

  }

 

   //继承接口后的实例

   contract FixedSupplyToken is ERC20Interface {

      string public constant symbol = "FIXED"; //单位

      string public constant name = "Example Fixed Supply Token"; //名称

      uint8 public constant decimals = 18; //小数点后的位数

      uint256 _totalSupply = 1000000; //发行总量

 

      // 智能合约的全部者

      address public owner;

 

      // 每一个帐户的余额

      mapping(address => uint256) balances;

 

      // 账户的全部者批准将金额转入另外一个账户。从上面的说明咱们能够得知allowed[被转移的帐户][转移钱的帐户]

      mapping(address => mapping (address => uint256)) allowed;

 

      // 只能经过智能合约的全部者才能调用的方法

      modifier onlyOwner() {

          if (msg.sender != owner) {

              throw;

          }

          _;

      }

 

      // 构造函数

      function FixedSupplyToken() {

          owner = msg.sender;

          balances[owner] = _totalSupply;

      }

 

      function totalSupply() constant returns (uint256 totalSupply) {

          totalSupply = _totalSupply;

      }

 

      // 特定帐户的余额

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

          return balances[_owner];

      }

 

      // 转移余额到其余帐户

      function transfer(address _to, uint256 _amount) returns (bool success) {

          if (balances[msg.sender] >= _amount

              && _amount > 0

              && balances[_to] + _amount > balances[_to]) {

              balances[msg.sender] -= _amount;

              balances[_to] += _amount;

              Transfer(msg.sender, _to, _amount);

              return true;

          } else {

              return false;

          }

      }

 

      //从一个帐户转移到另外一个帐户,前提是须要有容许转移的余额

      function transferFrom(

          address _from,

          address _to,

          uint256 _amount

      ) returns (bool success) {

          if (balances[_from] >= _amount

              && allowed[_from][msg.sender] >= _amount

              && _amount > 0

              && balances[_to] + _amount > balances[_to]) {

              balances[_from] -= _amount;

              allowed[_from][msg.sender] -= _amount;

              balances[_to] += _amount;

              Transfer(_from, _to, _amount);

              return true;

          } else {

              return false;

          }

      }

 

      //容许帐户从当前用户转移余额到那个帐户,屡次调用会覆盖

      function approve(address _spender, uint256 _amount) returns (bool success) {

          allowed[msg.sender][_spender] = _amount;

          Approval(msg.sender, _spender, _amount);

          return true;

      }

 

      //返回被容许转移的余额数量

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

          return allowed[_owner][_spender];

      }

  }

 

 

注意的是,最后的例子中allowed限定的第二维的参数是调用者的转移数量,而开始的例子是接收者的数量。

相关文章
相关标签/搜索