【Ethereum】以太坊ERC20與ERC233的區別
什麼是ERC223
ERC223是繼ERC20後推出的新標準,解決了ERC20中的一些問題
相關說明
ERC223開發的主要目標
1.合約中意外丟失token: 有兩種不同的方式來轉移ERC20 token: 1) 合約地址 2) 錢包地址 ,你需要呼叫transfer
傳送給錢包地址 或者 呼叫approve
在token合約然後呼叫transferFrom
在接收的合約來發送到合約。意外地,對合同地址呼叫 transfer
函式的呼叫將導致接收方合同內的token丟失,令牌將永遠不會被訪問。
2.無法處理進入的令牌交易: ERC20 token交易是合約內的transfer
函式的呼叫。當交易發生時,ERC20令牌合同不會通知接收方。此外,沒有辦法通過合同處理傳入的令牌交易,也沒有辦法拒絕任何不支援的令牌。
3.實際上,錢包地址和合同之間的ERC20令牌交易是兩個不同的交易:你應該在合約上呼叫approve
,然後再呼叫transferFrom
在另一份合約上,當你想你的token委託過去。
4.Ether交易和token交易行為不同: 開發ERC223的目標之一是進行與Ether交易相似token牌交易,以避免使用者在轉移token時出錯,併為契約開發人員更容易地與token交易進行互動。
ERC223的有利形勢
1.提供避免不符合傳送令牌的合同內的意外丟失令牌的可能性。
2.允許使用者通過一個函式呼叫傳送到任何地方他們的令牌。 收件人是否是合同是沒有區別的。 沒有必要了解令牌合約如何為常規使用者傳送令牌。
3.允許合同開發人員處理傳入令牌中的交易。
4.ERC223轉移到合同消耗比ERC20批准少2倍gas和transferFrom在接收機的合同。
5.允許將令牌存入單一交易的合約。 防止額外blockchain腫脹。
6.token交易類似於Ether交易。
ERC223令牌應通過以令牌合約的方式transfer
函式傳送,但請注意,如果接收方是合同或電子錢包地址,則不會有差異。如果接收者是錢包,則ERC223令牌傳輸將與ERC20傳輸相同。如果接收方是合約,ERC223令牌合約將嘗試在接收方合約中呼叫tokenFallback函式。如果接收方沒有tokenFallback函式,合約事務將失敗。tokenFallback函式是Ether事務的後備功能模擬,可用於處理傳入事務。有一種方法可以將bytes _data
_data
的令牌事務。它將通過令牌合約,並將通過接收方合同的tokenFallback
函式處理。還有一種方法可以在沒有資料引數的情況下使用ERC223令牌合約傳輸函式,或者使用沒有傳輸函式資料的ERC20 ABI。 在這種情況下_data將為空位元組陣列。
概述
以下描述標準功能,令牌合同和使用指定令牌的合同可以實施,以防止意外發送令牌到合同,並使令牌交易的行為像ether交易。
動機
ERC223解決了ERC20的一些問題:
1.無法處理接收合同中的進賬交易。
2.令牌可以傳送到合同中沒有設計不處理與標記工作,並可能會丟失。目前至少有四十萬美元的損失。
3.令牌交易應與Ethereum意識形態一致。當一個使用者轉賬的時候,必須自己執行transfer
.使用者存入合同或傳送到外部擁有的賬戶無關緊要。
這些將允許合同處理傳入令牌交易並防止意外發送的令牌被合同接受。
例如,分散式交換將不再需要強制使用者通過令牌合約來呼叫批准,然後通過從允許的令牌獲取正在呼叫transfer的呼叫存款。 令牌交易將在交易所合約內自動處理。
這裡最重要的是在執行合同交易時呼叫tokenFallback。
規格
Token
使用token的合約
方法
注意: 一個重要的一點是,如果合同開發人員希望他們的合同使用指定的令牌,那麼合同開發人員必須實現tokenFallback。
如果接收方未實現tokenFallback函式,則認為合同不是設計為使用令牌,那麼事務必須失敗,並且不會傳輸令牌。 在嘗試將Ether傳送到沒有實現function())的合同時,與Ether事務的類比是失敗的。
totalSupply
function totalSupply() constant returns (uint256 totalSupply)
獲取總量
name
function name() constant returns (string _name)
得到token的名字
symbol
function symbol() constant returns (bytes32 _symbol)
得到token的符號
decimals
function decimals() constant returns (uint8 _decimals)
得到token的小數點後幾位
balanceOf
function balanceOf(address _owner) constant returns (uint256 balance)
得到地址是_owner
的賬戶的餘額
transfer(address, uint)
function transfer(address _to, uint _value) returns (bool)
由於向後相容性原因,因為ERC20傳輸函式沒有位元組引數。如果_to
是合約,則此函式必須傳輸令牌並調_to中
的函式tokenFallback(address,uint256,bytes)
。如果_to
(接收方合同)中沒有實現tokenFallback
函式,則事務必須失敗,並且不會發生令牌的傳輸。
重要:將在接收方合約中呼叫的令牌備用功能必須命名為tokenFallback
,並使用引數address
,uint256
,bytes
。 此函式必須具有0xc0ee0b8a
簽名。
transfer(address, uint, bytes)
function transfer(address _to, uint _value, bytes _data) returns (bool)
當某人想要轉移令牌時總是呼叫這個函式。
如果_to
是合約,則此函式必須傳輸令牌並呼叫_to
中的函式tokenFallback (address, uint256, bytes)
。 如果_to
(接收方合同)中沒有實現tokenFallback
函式,則事務必須失敗,並且不會發生令牌的傳輸。
如果_to
是外部擁有的地址,則必須傳送事務,而不嘗試在_to
中執行tokenFallback
。
_data
可以附加到這個令牌交易中,它將永遠保持在塊狀(需要更多的gas)。 _data
可以是空的。
注意: 檢查_to
是合約還是地址的推薦方法是組裝_to
的程式碼。 如果_to
中沒有程式碼,那麼這是一個外部擁有的地址,否則就是一個合約。
重要: 將在接收方合約中呼叫的令牌備用功能必須命名為tokenFallback
,並使用引數address
, uint256
,bytes
。 此函式必須具有0xc0ee0b8a
簽名。
事件
Transfer
event Transfer(address indexed _from, address indexed _to, uint256 indexed _value, bytes _data)
當token轉移的時候觸發。
合約和token一起工作
function tokenFallback(address _from, uint _value, bytes _data)
令牌持有者傳送令牌時處理從令牌合同所呼叫的令牌傳輸的功能。 _from
是令牌傳送者,_value
是傳入令牌的數量,_data
是附加的資料,類似於Ether事務中的資料。 適用於以太交易的回退功能,並且不返回任何內容。
注意: msg.sender將是tokenFallback函式內的令牌合同。 過濾哪些令牌(通過令牌契約地址)傳送可能很重要。 令牌傳送者(誰發起了代幣交易的人)將_from thetokenFallback函式內。
重要: 這個函式必須命名為tokenFallback
,並使用引數地址uint256
,位元組來匹配函式簽名0xc0ee0b8a
。
示例程式碼
ERC223_Interface.sol
pragma solidity ^0.4.9;
/* 新的 ERC23 contract 介面檔案 */
contract ERC223 {
uint public totalSupply;
function balanceOf(address who) constant returns (uint);
function name() constant returns (string _name);
function symbol() constant returns (string _symbol);
function decimals() constant returns (uint8 _decimals);
function totalSupply() constant returns (uint256 _supply);
function transfer(address to, uint value) returns (bool ok);
function transfer(address to, uint value, bytes data) returns (bool ok);
function transfer(address to, uint value, bytes data, string custom_fallback) returns (bool ok);
event Transfer(address indexed from, address indexed to, uint value, bytes indexed data);
}
Receiver_Interface.sol
pragma solidity ^0.4.9;
/*
* Contract that is working with ERC223 tokens
*/
contract ContractReceiver {
struct TKN {
address sender; //呼叫合約的人
uint value;
bytes data;
bytes4 sig; //簽名
}
function tokenFallback(address _from, uint _value, bytes _data){
TKN memory tkn;
tkn.sender = _from;
tkn.value = _value;
tkn.data = _data;
uint32 u = uint32(_data[3]) + (uint32(_data[2]) << 8) + (uint32(_data[1]) << 16) + (uint32(_data[0]) << 24);
tkn.sig = bytes4(u);
/* tkn變數是Ether交易的msg變數的模擬
* tkn.sender是發起這個令牌交易的人(類似於msg.sender)
* tkn.value傳送的令牌數(msg.value的類比)
* tkn.data是令牌交易的資料(類似於msg.data)
* tkn.sig是4位元組的功能簽名
* 如果令牌事務的資料是一個函式執行
*/
}
}
ERC223_Token.sol
pragma solidity ^0.4.9;
import "./Receiver_Interface.sol";
import "./ERC223_Interface.sol";
/**
* ERC23 token by Dexaran
*
* https://github.com/Dexaran/ERC23-tokens
*/
/* https://github.com/LykkeCity/EthereumApiDotNetCore/blob/master/src/ContractBuilder/contracts/token/SafeMath.sol */
contract SafeMath {
uint256 constant public MAX_UINT256 =
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
function safeAdd(uint256 x, uint256 y) constant internal returns (uint256 z) {
if (x > MAX_UINT256 - y) throw;
return x + y;
}
function safeSub(uint256 x, uint256 y) constant internal returns (uint256 z) {
if (x < y) throw;
return x - y;
}
function safeMul(uint256 x, uint256 y) constant internal returns (uint256 z) {
if (y == 0) return 0;
if (x > MAX_UINT256 / y) throw;
return x * y;
}
}
//示例的智慧合約程式碼
contract ERC223Token is ERC223, SafeMath {
mapping(address => uint) balances;
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
// 獲取token的名稱
function name() constant returns (string _name) {
return name;
}
// 獲取token的符號
function symbol() constant returns (string _symbol) {
return symbol;
}
// 獲取token精確到小數點後的位數
function decimals() constant returns (uint8 _decimals) {
return decimals;
}
// 獲取token的釋出總量
function totalSupply() constant returns (uint256 _totalSupply) {
return totalSupply;
}
// 當用戶或其他合同想要轉移資金時呼叫的功能。
function transfer(address _to, uint _value, bytes _data, string _custom_fallback) returns (bool success) {
//如果to是合約
if(isContract(_to)) {
if (balanceOf(msg.sender) < _value) throw; //如果當前的餘額不夠就丟擲
balances[msg.sender] = safeSub(balanceOf(msg.sender), _value);//傳送者的餘額做減法
balances[_to] = safeAdd(balanceOf(_to), _value); //接收者的餘額做加法
ContractReceiver receiver = ContractReceiver(_to); //初始化接收合約,建構函式引數為接收者的合約地址
receiver.call.value(0)(bytes4(sha3(_custom_fallback)), msg.sender, _value, _data);
Transfer(msg.sender, _to, _value, _data);
return true;
}
else {
return transferToAddress(_to, _value, _data);
}
}
// 當用戶或其他合同想要轉移資金時呼叫的功能。
function transfer(address _to, uint _value, bytes _data) returns (bool success) {
if(isContract(_to)) {
return transferToContract(_to, _value, _data);
}
else {
return transferToAddress(_to, _value, _data);
}
}
// 類似於ERC20傳輸的標準功能傳輸,沒有_data。
// 由於向後相容性原因而增加。
function transfer(address _to, uint _value) returns (bool success) {
//類似於沒有_data的ERC20傳輸的標準功能傳輸
//由於向後相容性原因而增加
bytes memory empty;
if(isContract(_to)) {//如果是合約
return transferToContract(_to, _value, empty);
}
else {
return transferToAddress(_to, _value, empty);
}
}
//組裝定地址位元組碼。 如果存在位元組碼,那麼_addr是一個合約。
function isContract(address _addr) private returns (bool is_contract) {
uint length;
assembly {
//檢索目標地址上的程式碼大小,這需要彙編
length := extcodesize(_addr)
}
return (length>0);
}
//當傳遞目標是一個地址時呼叫函式
function transferToAddress(address _to, uint _value, bytes _data) private returns (bool success) {
if (balanceOf(msg.sender) < _value) throw;
balances[msg.sender] = safeSub(balanceOf(msg.sender), _value);
balances[_to] = safeAdd(balanceOf(_to), _value);
Transfer(msg.sender, _to, _value, _data);
return true;
}
//當傳遞目標是一個合約時呼叫函式
function transferToContract(address _to, uint _value, bytes _data) private returns (bool success) {
if (balanceOf(msg.sender) < _value) throw;
balances[msg.sender] = safeSub(balanceOf(msg.sender), _value);
balances[_to] = safeAdd(balanceOf(_to), _value);
ContractReceiver receiver = ContractReceiver(_to);
receiver.tokenFallback(msg.sender, _value, _data); //必須要呼叫這個回撥
Transfer(msg.sender, _to, _value, _data);
return true;
}
//得到_owner的餘額
function balanceOf(address _owner) constant returns (uint balance) {
return balances[_owner];
}
}