以太坊:什麼是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限定的第二維的參數是調用者的轉移數量,而開始的例子是接收者的數量。

相關文章
相關標籤/搜索