教程网

您现在的位置是: 首页 > 聚焦

以太坊通证标准:ERC20系列、ERC721系列、ERC865描述和定义

以太坊通证标准:ERC20系列、ERC721系列、ERC865描述和定义
1、摘要通过本文学习,了解ERC定义及知名ERC协议标准,如ERC20以及ERC223、ERC621.ERC777.ERC827.ERC1410协议; ERC721以及 ERC875.ERC1155.ERC998协议,ERC 865等描述和定义。2、

1、摘要

通过本文学习,了解ERC定义及知名ERC协议标准,如ERC20以及ERC223、ERC621.ERC777.ERC827.ERC1410协议; ERC721以及 ERC875.ERC1155.ERC998协议,ERC 865等描述和定义。

2、ERC的定义及标准列表

ERC代表“Etuereum Request for Comment”,这是Ethereum版的意见征求稿 (RFC),RFC是由互联网工程任务组制定的一个概念。 RFC中的备忘录包含技术和组织注意事项。 对于ERC,意见征求稿中包括一些关于以太坊网络建设的技术指导。

ERC是Ethereum开发者为以太坊社区编写的。 因此,ERC的创建流程中包括开发人员。 为了创建一个以太坊平台的标准,开发人员应当提交了一个以太坊改进方案(EIP), 改进方案中包括协议规范和合约标准。 一旦EIP被委员会批准并最终确定,它就成为ERC。 EIP的完整列表可以在GITHUB网址找到。

EIP有4种状态:

草稿(Draft) - 处于打开状态,便于考察讨论;

接受(Accepted) - 即将被接受,例如将包含在下一个硬分叉中;

定稿(Final)- 在上一个硬分叉中被接受,已定稿;

延期(Deferred)- 不会马上被接受,但也许在将来的硬分叉版本会考虑。

最终确定的EIP为以太坊开发者提供了一套可实施的标准。 这使得智能合约可以遵循这些通用的接口标准来构建。

ERC-20是整个加密社区中最为人熟知的标准,在Ethereum平台之上发布的大多数通证(token)都使用它。

3、ERC20系列 – ERC20、ERC223、ERC621、ERC777、ERC827、ERC1400

3.1 ERC20标准

状态:

定稿(Final)

提交记录:

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

标准说明:

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

推荐样例:

https://github.com/OpenZeppelin/openzeppelin-solidity/tree/master/contracts/token/ERC20

ERC-20标准中定义了以下函数接口:

totalSupply():

返回代币供给总量

balanceOf(address _owner):

返回_owner的帐户余额

transfer(address _to,uint256 _value):

并将数量为_value的代币转入地址_to并触发transfer事件

transferFrom(address _from,address _to,uint256_value):

将地址_from中的_value数量的代币转入地址_to ,并触发transfer事件

approve(address _spender,uint256 _value):

允许_spender提取限额_value的代币

allowance(address _owner,address _spender):

返回_spender可从_owner提款的代币数量上限

ERC-20于2015年提出并于2017年9月正式实施。这是代币标准化的一个很好的起点。 然而,开发者社区 已经注意到它存在一些缺陷和漏洞,此外,还有一些场景它不能很好的满足。因此陆续提出了其他的ERC标准。

3.2 ERC223

状态:

草稿(Draft)

提交记录:

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

标准说明:

https://github.com/Dexaran/ERC223-token-standard

推荐样例:

https://github.com/Dexaran/ERC223-token-standard/tree/Recommended

开发人员Dexaran在一篇文章中详细描述了ERC20不适合的两种场景:

“在ERC20中执行交易有两种方式:transfer函数,approve + transferFrom机制,通证余额只是通证合约中的一个变量。

通证的交易是合约内部变量的变化。 转出账户的余额将减少,转入账户的余额将增加。

交易发生时, transfer()函数不会通知转入账户。 因此转入账户将无法识别传入的交易! 我写了一个例子,可以展示这一导致未处理的交易和资金损失的过程 。

因此,如果接收账户是合约账户,那么必须使用approve + transferFrom机制来发送通证。

如果接受账户是外部帐户,则必须通过transfer函数发送通证。 如果选择了错误的机制,通证将卡在合约内(合约将不会识别交易),没有办法来提取这些卡壳的通证。“

他对这个问题提出的解决方案包含在ERC-223中 。 它与ERC-20标准非常相似,但解决了上述问题。当通证转移到智能合约账户时,该合约的特殊函数tokenFallback() 允许接收方合约拒绝令牌或触发进一步的操作。 大多数情况下,这可以用来代替approve()函数。

函数接口:

transfer(address _to, uint _value):

会区分代币是发往外部账户地址还是发往智能合约地址。

transfer(address _to, uint _value, bytes _data):

会区分代币是发往外部账户地址还是发往智能合约地址,还可以传送数据。

1、如果_to是合约,则此函数必须传输令牌并调_to中的函数tokenFallback(address,uint256.bytes)。

2、如果_to(接收方合同)中没有实现tokenFallback函数,则事务必须失败,并且不会发生令牌的传输。

3、如果_to是外部拥有的地址,则必须发送事务,而不尝试在_to中执行tokenFallback。

4、_data可以附加到这个令牌交易中,它将永远保持在块状(需要更多的gas)。 _data可以是空的。

注意:检查_to是合约还是地址的推荐方法是组装_to的代码。 如果_to中没有代码,那么这是一个外部拥有的地址,否则就是一个合约。

//assemble the given address bytecode. If bytecode exists then the _addr is a contract.

function isContract(address _addr) private view returns (bool is_contract) {

uint length;

assembly {

//retrieve the size of the code on target address, this needs assembly

length := extcodesize(_addr)

}

return (length>0);

}

function tokenFallback(address _from, uint _value, bytes _data)

_from是令牌发送者,_value是传入令牌的数量,_data是附加的数据,类似于Ether事务中的数据。 适用于以太交易的回退功能,并且不返回任何内容。

tokenFallback — 令牌持有者发送令牌时处理从令牌合同所调用的令牌传输的功能

注意:msg.sender将是tokenFallback函数内的令牌合同。 过滤哪些令牌(通过令牌契约地址)发送可能很重要。 令牌发送者(谁发起了代币交易的人)在_from thetokenFallback函数内。

示例代码:

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

}

}

3.3 ERC621

状态:

草稿(Draft)

提交记录:

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

标准说明:

推荐样例:

https://github.com/skmgoldin/EIP621Token/blob/master/contracts/EIP621Token.sol

ERC-621是ERC-20标准的扩展。 它增加了两个额外的功能, increaseSupply和decreaseSupply 。这可以增加和减少流通中的令牌供应。 ERC-20只允许单一的通证发放事件。 这将供应量限制在一个固定的不可改变的数目。 ERC-621建议totalSupply应当是可修改的。

接口函数说明:

increaseSupply(uint value, address to):

可以给特定账户to增加value值的供应量,代币总供应量totalSupply也同步增加;

decreaseSupply(uint value, address from):

可以给特定账户to减少value值的账户余额,代币总供应余额totalSupply也同步减少;

3.4 ERC777

状态:

草稿(Draft)

提交记录:

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

标准说明:

https://eips.ethereum.org/EIPS/eip-777

推荐样例:

https://github.com/jacquesd/ERC777/tree/devel/contracts

功能描述

本标准用于提升ERC20标准的使用范围。这个标准的主要优点有:

可以同使用Ether一样的原理使用ERC777通证,通证发送采用函数send(dest, value, data)的。

不管是合约还是常规的地址都可以控制和拒绝那些通过注册tokensToSend 钩函数发送的通证(通过在钩函数内部执行reverting回滚函数触发拒绝。)

不管是合约还是常规的地址都可以控制和拒绝那些通过注册tokensToSend 钩函数接收的通证(通过在钩函数内部执行reverting回滚函数触发拒绝。)

 1/5    1 2 3 4 5 下一页 尾页