什么是 ERC20
ERC-20 标准是在2015年11月份推出的,使用这种规则的代币,表现出一种通用的和可预测的方式。
简单地说,任何 ERC-20 代币都能立即兼容以太坊钱包(几乎所有支持以太币的钱包,包括Jaxx、MEW、imToken等,也支持 erc-20的代币),由于交易所已经知道这些代币是如何操作的,它们可以很容易地整合这些代币。这就意味着,在很多情况下,这些代币都是可以立即进行交易的。
标准化非常有利,也就意味着这些资产可以用于不同的平台和项目,否则只能用在特定的场合。
代币(Token)是区块链中定义价值的方式,用于标定金融或数字资产。在以太坊上,代币使用相同的标准,这样代币之间的兑换和DAPP支持就会变得容易。
标准规定了哪些内容
ERC20 是各个代币的标准接口。ERC20 代币仅仅是以太坊代币的子集。为了充分兼容 ERC20,开发者需要将一组特定的函数(接口)集成到他们的智能合约中,以便在高层面能够执行以下操作:
- 获得代币总供应量
- 获得账户余额
- 转让代币
- 批准花费代币
ERC20 让以太坊区块链上的其他智能合约和去中心化应用之间无缝交互。一些具有部分但非所有ERC20标准功能的代币被认为是部分 ERC20兼容,这还要视其具体缺失的功能而定,但总体是它们仍然很容易与外部交互。
ERC20 标准
ERC20 标准定义了一个兼容协议, 需要实现的函数. 具体如下.
contract ERC20Interface {
// 代币名称
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
// 0.0000000000000000001 个代币
// 法币总量
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 approve(address spender, uint tokens) public returns (bool success);
// 实现代币交易,转账
function transfer(address to, 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
ERC20 并不是完美的
ERC-20标准还有待完善。
- 其中一个障碍是,将令牌直接发送给令牌的智能合同将导致资金损失。这是因为一个令牌的合同只会跟踪和分配资金。例如,当您从钱包中向另一个用户发送令牌时,该钱包将调用令牌的合约来更新数据库。所以如果您试图将令牌直接传输到令牌的合约中,那么由于该令牌的合约无法响应,所以金钱就“丢失”了。
- ERC20标准无法通过接收方合同处理传入的交易。这是该令牌存在的最大问题,也是开发者一直希望改进的地方。ERC20令牌无法将令牌发送给一个与这些令牌不兼容的契约,也正因为这样,部分资金存在丢失的风险。
- Reddit上的一篇文章指出,由于被发送到“错误”的合同上,大约价值40万美元的ERC20令牌被困,这对整个以太坊生态系统而言是一个巨大的威胁。幸运的是,ERC223令牌可以解决这一难题,前提是该令牌能够获得批准并被引入。
抽象
以下标准允许在智能合约中实施标记的标记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)
发行代币demo
代码
pragma solidity ^0.4.20;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }
contract TokenERC20 {
string public name;
string public symbol;
uint8 public decimals = 18; // decimals 可以有的小数点个数,最小的代币单位。18 是建议的默认值
uint256 public totalSupply;
// 用mapping保存每个地址对应的余额
mapping (address => uint256) public balanceOf;
// 存储对账号的控制
mapping (address => mapping (address => uint256)) public allowance;
// 事件,用来通知客户端交易发生
event Transfer(address indexed from, address indexed to, uint256 value);
// 事件,用来通知客户端代币被消费
event Burn(address indexed from, uint256 value);
/**
* 初始化构造
*/
constructor(uint256 initialSupply, string tokenName, string tokenSymbol) public {
totalSupply = initialSupply * 10 ** uint256(decimals); // 供应的份额,份额跟最小的代币单位有关,份额 = 币数 * 10 ** decimals。
balanceOf[msg.sender] = totalSupply; // 创建者拥有所有的代币
name = tokenName; // 代币名称
symbol = tokenSymbol; // 代币符号
}
/**
* 代币交易转移的内部实现
*/
function _transfer(address _from, address _to, uint _value) internal {
// 确保目标地址不为0x0,因为0x0地址代表销毁
require(_to != 0x0);
// 检查发送者余额
require(balanceOf[_from] >= _value);
// 确保转移为正数个
require(balanceOf[_to] + _value > balanceOf[_to]);
// 以下用来检查交易,
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// Subtract from the sender
balanceOf[_from] -= _value;
// Add the same to the recipient
balanceOf[_to] += _value;
emit Transfer(_from, _to, _value);
// 用assert来检查代码逻辑。
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* 代币交易转移
* 从创建交易者账号发送`_value`个代币到 `_to`账号
*
* @param _to 接收者地址
* @param _value 转移数额
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
/**
* 账号之间代币交易转移
* @param _from 发送者地址
* @param _to 接收者地址
* @param _value 转移数额
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]); // Check allowance
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
/**
* 设置某个地址(合约)可以交易者名义花费的代币数。
*
* 允许发送者`_spender` 花费不多于 `_value` 个代币
*
* @param _spender The address authorized to spend
* @param _value the max amount they can spend
*/
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* 设置允许一个地址(合约)以交易者名义可最多花费的代币数。
*
* @param _spender 被授权的地址(合约)
* @param _value 最大可花费代币数
* @param _extraData 发送给合约的附加数据
*/
function approveAndCall(address _spender, uint256 _value, bytes _extraData)
public
returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
if (approve(_spender, _value)) {
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
}
/**
* 销毁创建者账户中指定个代币
*/
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
balanceOf[msg.sender] -= _value; // Subtract from the sender
totalSupply -= _value; // Updates totalSupply
emit Burn(msg.sender, _value);
return true;
}
/**
* 销毁用户账户中指定个代币
*
* Remove `_value` tokens from the system irreversibly on behalf of `_from`.
*
* @param _from the address of the sender
* @param _value the amount of money to burn
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value); // Check if the targeted balance is enough
require(_value <= allowance[_from][msg.sender]); // Check allowance
balanceOf[_from] -= _value; // Subtract from the targeted balance
allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance
totalSupply -= _value; // Update totalSupply
emit Burn(_from, _value);
return true;
}
}
在remix中编译运行上面的上面的智能合约,键入参数,配合metamask插件,
在METAMASK中查看自己的代币:
ERC223要解决的首要问题是什么?
自从引入ERC20令牌标准以来,几乎所有的基于以太坊的令牌都成功的接受了这个新标准。然而其自身的缺点需要及时解决,这便是ERC223令牌诞生的原因。
防止丢失
ERC223令牌标准将向现有的ERC20标准引入一个新功能,以防止意外转移的发生。ERC223令牌标准可以防止令牌在以太坊网络上丢失。
困难的转换
假设 ERC223 令牌标准能够取代ERC20成为新的标准,现有令牌的发行方需要做一些艰难的决定。因为,从现实情况来看,不管用何种方式,从ERC20转换到ERC223是不可能的,同样的,所有ERC20令牌都需要在ERC223标准下重新部署。这也意味着任何交易平台的上市都需要更新他们的信息和地址。这是一个艰苦的过程,这也就意味着,在未来很少有现有的令牌被有效地转换为ERC223。
正如Alex van de Sande在Reddit上指出的那样,“更方便”的过程可能是创建新的令牌,它们是通过持有旧令牌的合同支持的。这可能是大多数项目最合理的选择,但只有时间才能确定哪些选项将被实施。