以太坊 TOKEN
EIP全称 Ethereum Imporvement Proposals(以太坊改进建议), 是以太坊开发者社区提出的改进建议, EIP 可以是Ethereum 生态中任意领域的改进, 比如新特性、ERC、协议改进、编程工具等等。EIP 是以太坊生态演进的公告牌,可以在以下地址查询到当前所有的 EIPs:https://eips.ethereum.org/all
ERC全称 Ethereum Request For Comment(以太坊意见征求稿), 用以记录以太坊上应用级的各种开发标准和协议(application-level standards and conventions)。如典型的 Token 标准(ERC20,ERC721)、名字注册(ERC26,ERC13), URI 范式(ERC67), Library/Package 格式(EIP82), 钱包格式(EIP75,EIP85)。ERC 协议标准是影响以太坊发展的重要因素, 像 ERC20, ERC223, ERC721, ERC777 等, 都是对以太坊生态产生了很大影响。
可以看出:所有的 ERC 都是 EIP。反之则未必。以太坊开发者希望利用技术社区的众包智慧来决定 Ethereum 系统的未来走向, 而不是集中在几个硏发人员手里。因此每个人都可以在 Ethereum 社区提出任何好的 EIP。
一、ERC20
ERC20是是 2015 年 11 月份推出的一种技术标准,我们经常看到的 EOS,USDT(ERC20),OMG 等就是基于 ERC20 标准开发的代币;
ERC20 的优势 :
- ERC20 标准规定了各个代币的基本功能,非常方便第三方使用;
- 基于 ERC20 标准开发的同种代币价值都是相同的,它们可以进行互换;
- ERC20 代币能兼容 ETH 钱包,也就是说,ETH 的钱包地址可以接收所有的 ERC20 代币。因此 ERC20 代币能被交易所整合,立即进行交易。
ERC20 的劣势 : - ERC20 标准无法通过接收方合同处理传入的交易。这是该令牌存在的最大问题,也是开发者一直希望改进的地方;
- ERC20 令牌无法将令牌发送给一个与这些令牌不兼容的契约,也正因为这样,部分资金存在丢失的风险。
实现:
pragma solidity ^0.5.6;
contract ERC20 {
uint256 public totalSupply;
string public name;
string public symbol;
uint8 public decimals;
mapping (address => uint256) private balances;
mapping (address => mapping (address => uint256)) private allowed;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
constructor(
uint256 _initialAmount,
string memory _tokenName,
uint8 _decimalUnits,
string memory _tokenSymbol
) public {
balances[msg.sender] = _initialAmount;
totalSupply = _initialAmount;
name = _tokenName;
decimals = _decimalUnits;
symbol = _tokenSymbol;
}
function transfer(address _to, uint256 _value) public returns (bool success) {
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
uint256 allowance = allowed[_from][msg.sender];
require(balances[_from] >= _value && allowance >= _value);
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}
二、ERC721
和 ERC20 一样,ERC721 同样是一个代币标准,ERC721 的官方解释是是“Non-Fungible Tokens”,英文简写为”NFT”,翻译过来即不可互换的非同质 Token。简单地说,就是每个 Token 都是独一无二的,是不能互换的;
之前大火的撸猫游戏 CryptoKitties 也是基于 ERC721 标准开发的,CryptoKitties 中的猫都是 ERC721 的 Token,不同基因的猫其实只是不同属性的 Token,正是由于 ERC721 的独一无二性(NFT)让 CryptoKitties 的猫变得极具收藏价值,单只猫的价值更是达到了 1000 个 ETH!!
ERC20 和 ERC721 的区别:
- Token 之间是否可以互换,基于 ERC20 标准开发的 Token 没有价值的区别,是可以互换的,基于 ERC721 标准开发的 Token 则是独一无二,不可以互换的;
- Token 是否可以分割,ERC20 的 Token 是可以分割的,而 ERC721 的 Token 最小的单位为 1,它是不能分割的
实现:
pragma solidity ^0.5.6;
/* 一种非同质代币合约,doc:eips.ethereum.org/EIPS/eip-721 */
contract ERC721 {
using AddressUtils for address;
mapping (uint256 => address) private idToOwner;
mapping (uint256 => address) private idToApproval;
mapping (address => uint256) private ownerToNFTokenCount;
mapping (address => mapping (address => bool)) private ownerToOperators;
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
modifier validNFToken(uint256 _tokenId){
require(idToOwner[_tokenId] != address(0), "NOT_VALID_TOKEN");
_;
}
modifier canOperate(uint256 _tokenId){
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == msg.sender || ownerToOperators[tokenOwner][msg.sender], "NOT_OWNER_OR_OPERATOR");
_;
}
modifier canTransfer(uint256 _tokenId){
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == msg.sender
|| idToApproval[_tokenId] == msg.sender
|| ownerToOperators[tokenOwner][msg.sender],
"NOT_OWNER_APPROWED_OR_OPERATOR");
_;
}
// 核心接口
function balanceOf(address _owner) public view returns (uint256){
require(_owner != address(0), "ZERO_ADDRESS");
return ownerToNFTokenCount[_owner];
}
function ownerOf(uint256 _tokenId) public view returns (address _owner){
_owner = idToOwner[_tokenId];
require(_owner != address(0), "NOT_VALID_NFT");
}
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) public payable{
_safeTransferFrom(_from, _to, _tokenId, data);
}
function safeTransferFrom(address _from, address _to, uint256 _tokenId) public payable{
_safeTransferFrom(_from, _to, _tokenId, "");
}
function transferFrom(address _from, address _to, uint256 _tokenId) public payable
canTransfer(_tokenId)
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == _from, "NOT_OWNER");
require(_to != address(0), "ZERO_ADDRESS");
_transfer(_to, _tokenId);
}
function approve(address _approved, uint256 _tokenId) public payable
canOperate(_tokenId)
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(_approved != tokenOwner, "IS_OWNER");
idToApproval[_tokenId] = _approved;
emit Approval(tokenOwner, _approved, _tokenId);
}
function setApprovalForAll(address _operator, bool _approved) public{
ownerToOperators[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
function getApproved(uint256 _tokenId) public view validNFToken(_tokenId) returns (address){
return idToApproval[_tokenId];
}
function isApprovedForAll(address _owner, address _operator) public view returns (bool){
return ownerToOperators[_owner][_operator];
}
function supportsInterface(bytes4 _interfaceID) private view returns (bool);
//元信息扩展,可选的
string public name;
string public symbol;
mapping (uint256 => string) tokenURIs;
function tokenURI(uint256 _tokenId) public view validNFToken(_tokenId) returns (string memory){
return tokenURIs[_tokenId];
}
// 枚举信息扩展,可选的
uint256[] internal tokens;
mapping(uint256 => uint256) private idToIndex;
mapping(address => uint256[]) private ownerToIds;
mapping(uint256 => uint256) private idToOwnerIndex;
function totalSupply() external view returns (uint256){
return tokens.length;
}
function tokenByIndex(uint256 _index) public view returns (uint256){
require(_index < tokens.length, "INVALID_INDEX");
return tokens[_index];
}
function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256){
require(_index < ownerToIds[_owner].length, "INVALID_INDEX");
return ownerToIds[_owner][_index];
}
function _transfer(address _to,uint256 _tokenId)internal{
address from = idToOwner[_tokenId];
_clearApproval(_tokenId);
_removeNFToken(from, _tokenId);
_addNFToken(_to, _tokenId);
emit Transfer(from, _to, _tokenId);
}
function _mint(address _to,uint256 _tokenId)internal{
require(_to != address(0), "ZERO_ADDRESS");
require(idToOwner[_tokenId] == address(0), "NFT_ALREADY_EXISTS");
_addNFToken(_to, _tokenId);
emit Transfer(address(0), _to, _tokenId);
// for 枚举信息扩展
tokens.push(_tokenId);
idToIndex[_tokenId] = tokens.length - 1;
}
// for 枚举信息扩展
function _setTokenUri(uint256 _tokenId,string memory _uri)internal validNFToken(_tokenId){
tokenURIs[_tokenId] = _uri;
}
function _burn(uint256 _tokenId)internal validNFToken(_tokenId){
address tokenOwner = idToOwner[_tokenId];
_clearApproval(_tokenId);
_removeNFToken(tokenOwner, _tokenId);
emit Transfer(tokenOwner, address(0), _tokenId);
// for 元信息扩展
if (bytes(tokenURIs[_tokenId]).length != 0)
delete tokenURIs[_tokenId];
// for 枚举信息扩展
uint256 tokenIndex = idToIndex[_tokenId];
uint256 lastTokenIndex = tokens.length - 1;
uint256 lastToken = tokens[lastTokenIndex];
tokens[tokenIndex] = lastToken;
tokens.pop();
idToIndex[lastToken] = tokenIndex;
idToIndex[_tokenId] = 0;
}
function _removeNFToken(address _from,uint256 _tokenId)internal{
require(idToOwner[_tokenId] == _from, "NOT_OWNER");
ownerToNFTokenCount[_from] = ownerToNFTokenCount[_from] - 1;
delete idToOwner[_tokenId];
// for 枚举信息扩展
uint256 tokenToRemoveIndex = idToOwnerIndex[_tokenId];
uint256 lastTokenIndex = ownerToIds[_from].length - 1;
if (lastTokenIndex != tokenToRemoveIndex){
uint256 lastToken = ownerToIds[_from][lastTokenIndex];
ownerToIds[_from][tokenToRemoveIndex] = lastToken;
idToOwnerIndex[lastToken] = tokenToRemoveIndex;
}
ownerToIds[_from].pop();
}
function _addNFToken(address _to,uint256 _tokenId)internal {
require(idToOwner[_tokenId] == address(0), "NFT_ALREADY_EXISTS");
idToOwner[_tokenId] = _to;
ownerToNFTokenCount[_to] += 1;
// for 枚举信息扩展
ownerToIds[_to].push(_tokenId);
idToOwnerIndex[_tokenId] = ownerToIds[_to].length - 1;
}
function _getOwnerNFTCount(address _owner)internal view returns (uint256){
return ownerToIds[_owner].length;
}
function _safeTransferFrom(address _from,address _to,uint256 _tokenId,bytes memory _data) private
canTransfer(_tokenId) validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == _from, "NOT_OWNER");
require(_to != address(0), "ZERO_ADDRESS");
_transfer(_to, _tokenId);
if (_to.isContract()){
bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
require(retval == bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")), "NOT_ABLE_TO_RECEIVE_NFT");
}
}
function _clearApproval(uint256 _tokenId)private{
if (idToApproval[_tokenId] != address(0))
delete idToApproval[_tokenId];
}
}
interface ERC721TokenReceiver {
// return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
function onERC721Received(address _operator,address _from,uint256 _tokenId,bytes calldata _data) external returns(bytes4);
}
library AddressUtils {
function isContract (address _addr) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(_addr) }
return size > 0;
}
}