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
標準化很是有利,也就意味着這些資產能夠用於不一樣的平臺和項目,不然只能用在特定的場合。
如下標準容許在智能合約中實施標記的標記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)
返回token的總供應量。
function totalSupply() constant returns (uint256 totalSupply)
返回地址是_owner的帳戶的帳戶餘額。
function balanceOf(address _owner) constant returns (uint256 balance)
轉移_value的token數量到的地址_to,而且必須觸發Transfer事件。 若是_from賬戶餘額沒有足夠的令牌來支出,該函數應該被throw。
建立新令牌的令牌合同應該在建立令牌時將_from地址設置爲0x0觸發傳輸事件。
注意 0值的傳輸必須被視爲正常傳輸並觸發傳輸事件。
function transfer(address _to, uint256 _value) returns (bool success)
從地址_from發送數量爲_value的token到地址_to,必須觸發Transfer事件。
transferFrom方法用於提取工做流,容許合同代您轉移token。這能夠用於例如容許合約代您轉讓代幣和/或以子貨幣收取費用。除了_from賬戶已經經過某種機制故意地受權消息的發送者以外,該函數**應該**throw。
注意 0值的傳輸必須被視爲正常傳輸並觸發傳輸事件。
function transferFrom(address _from, address _to, uint256 _value) returns (bool success)
容許_spender屢次取回您的賬戶,最高達_value金額。 若是再次調用此函數,它將以_value覆蓋當前的餘量。
注意:爲了阻止向量攻擊,客戶端須要確認以這樣的方式建立用戶接口,即將它們設置爲0,而後將其設置爲同一個花費者的另外一個值。雖然合同自己不該該強制執行,容許向後兼容之前部署的合同兼容性
function approve(address _spender, uint256 _value) returns (bool success)
返回_spender仍然被容許從_owner提取的金額。
function allowance(address _owner, address _spender) constant returns (uint256 remaining)
當token被轉移(包括0值),必須被觸發。
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)
實施
在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限定的第二維的參數是調用者的轉移數量,而開始的例子是接收者的數量。