什么是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
附加到类似于连接到Ether 事务的_data
的令牌事务。它将通过令牌合约,并将通过接收方合同的tokenFallback
函数处理。还有一种方法可以在没有数据参数的情况下使用ERC223令牌合约传输函数,或者使用没有传输函数数据的ERC20 ABI。 在这种情况下_data将为空字节数组。
概述
ERC: 223
Title: Token standard
Author: Dexaran, dexaran820@gmail.com
Status: Draft
Type: ERC
Created: 5-03.2017
Resolution: https://github.com/Dexaran/ERC223-token-standard
Recommended implementation: https://github.com/Dexaran/ERC223-token-standard/tree/Recommended
以下描述标准功能,令牌合同和使用指定令牌的合同可以实施,以防止意外发送令牌到合同,并使令牌交易的行为像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];
}
}
参考资料
- https://github.com/ethereum/EIPs/issues/223
- https://github.com/Dexaran/ERC223-token-standard/tree/Recommended
- https://www.reddit.com/r/ethereum/comments/60ql37/attention_be_careful_using_ethereum_tokens/
- http://www.jinse.com/news/ethereum/43264.html
- https://www.reddit.com/r/ethereum/comments/6m9rnu/erc20_vs_erc223_eli5/