Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.
: M, H0 ^( @3 M2 d- |- XERC721是什么
" ]3 l- Z# s0 g, D3 t5 A在创建代币一篇,我们讲到过ERC20代币,
; z8 m8 \) A( U1 o$ @8 u/ W和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。- p+ i0 R: J) \# N' X

# g' p7 [6 e8 r$ w. ~9 ~ERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。
4 |( _- \- ?' ]8 J$ D3 D
5 K7 `% s- J" s6 f2 V4 ]
那怎么理解非同质代币呢?
% O: D, g- z2 R8 q* X' r) U非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。  f4 k  R( E) S- o0 i9 ~
ERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。
$ k! c2 K6 r) V$ n/ c; K% t5 q6 B" k
如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。
& M4 v9 F5 u( ^* q& ^: G# Z2 b2 c
- B3 Q- e* g& d) \
非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。9 `" O7 m# i# j# @. t
ERC721标准9 t' D  F* D; I: b
ERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:
. q- j/ J! b# L' p- O( K; K( hpragma solidity ^0.4.20;
" _0 e" `* `. p* e- g  Y8 Uinterface ERC721 /* is ERC165 */ {2 j* o2 V4 }! y* E- u6 T
    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);$ k6 U/ Y+ G$ w" T6 d
    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
+ G/ q+ O" j8 g) C( I0 _' F% c4 J! Z- j    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);7 g/ F2 T% S0 s8 P* T
    function balanceOf(address _owner) external view returns (uint256);
( Y5 K- L& H4 f2 P    function ownerOf(uint256 _tokenId) external view returns (address);( I$ B4 U3 X  \) k, B$ K" m3 R
   
! V. A( Z% r; M1 G' R+ t! K( M    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
5 Y" {& _# v2 I& F    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;2 P; I8 x# I; I1 u8 F( h
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
% `+ z/ Y8 D8 j' R; A    7 X! @7 T* E' i) ?- q
    function approve(address _approved, uint256 _tokenId) external payable;  l4 o5 i- }( Q" }3 u( I
    function setApprovalForAll(address _operator, bool _approved) external;9 u$ C+ ^7 x9 {- |: T
    function getApproved(uint256 _tokenId) external view returns (address);
+ W5 y4 [7 I/ U$ G    function isApprovedForAll(address _owner, address _operator) external view returns (bool);5 d: {& q$ w% U% w! t( R; q
}
! d: ^2 y0 \8 H8 S/ K接口说明:
) N5 V9 v, X5 d3 m0 _: U- ]4 S  P0 d6 Y3 P) L4 H1 ~# i; G
balanceOf(): 返回由_owner 持有的NFTs的数量。
5 @# p. I% V1 P1 p1 `! Q2 R( ~+ x: T2 m6 Q8 W( j* t
ownerOf(): 返回tokenId代币持有者的地址。
4 m. L8 ^+ ~' w* D1 P5 C9 k5 i/ m- Q0 p! c# [7 a% C  D! g0 W- |
approve(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。2 c0 G4 K- o1 s, T7 C0 O9 v1 P  R
( ^& e5 R6 m: p+ Y1 P9 R3 u7 b
setApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。/ J; t  w& F+ X
' i5 I3 d* V5 l  \
getApproved()、isApprovedForAll(): 用来查询授权。
3 }; Y6 Y6 t$ j, Q6 |7 y# d

' R* P5 o- L+ F* s8 G  osafeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:: u- a2 x+ G& O' i  H3 F$ ^: Y- C# E% I

$ Z- B( @7 c; Q8 B( `) q
  `# |0 M0 y& G$ f/ q8 y. a
1 E# K. q9 X+ h
调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在监测的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。一个可接收NFT的合约必须实现ERC721TokenReceiver接口:5 m# Q  |6 g1 r4 p

# ]* @1 ^- E8 Ninterface ERC721TokenReceiver {% r  O8 R* c* {" |0 G* U
    /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
0 |. ^. J4 O% T# z; H2 I    function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);9 R2 j+ s+ r, f; }% l' h- h; q  \
}
. V4 V8 |4 {( ltransferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。- _" S6 k; ~1 A$ p! |; ?

' A! J! i' J1 kERC165 标准
# U: y5 o: T/ Q  c/ S; ?, e2 cERC721标准同时要求必须符合ERC165标准 ,其接口如下:
% t/ V" m* `, L& }# I* s: ~interface ERC165 {( c8 \4 V  Y& i: L7 }( a
    function supportsInterface(bytes4 interfaceID) external view returns (bool);4 I0 w5 E# X$ h- p8 j% R$ q; I+ B
}/ H: `9 a2 {4 {- S+ q% x9 \
ERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。: a. P7 o# e- {8 _
interfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));或ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。0 t: I7 f9 [* d5 q: q* k! F4 l
关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案。" o( ^9 H" Z6 `% L+ Y- Z; x
可选实现接口:ERC721Metadata* E  \7 X! l1 M8 u" v8 F8 O$ x
ERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。- p+ R  N9 w% ~' T, R
其接口定义如下:
7 }/ \5 m2 P) w2 k* pinterface ERC721Metadata /* is ERC721 */ {
+ G4 a, k% h& o) N    function name() external pure returns (string _name);. O( [3 C# ~% A/ x" I) ^7 @
    function symbol() external pure returns (string _symbol);
8 k! K) C) C. R0 Q6 P$ s: [' Z    function tokenURI(uint256 _tokenId) external view returns (string);  ~3 c3 d& e) Y0 R
}
. e* @9 n" n, p1 M接口说明:' [/ {: y4 d# X! b! w5 E
name(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:" m5 {; }* Y; Z- B. e+ q
: t- G% U6 I- a' K, f
{- l' D  x4 |9 N( ^
    "title": "Asset Metadata",2 t5 n7 a- w6 o* Q* I/ m; ]
    "type": "object",
# ^- b7 u9 p- L+ |' \2 j, Q7 y    "properties": {8 I3 z  x4 Z; ^# l. Z0 c
        "name": {" X% X$ [+ W, z  s+ }! Y, C
            "type": "string",! C! K& F6 l1 ?9 r
            "description": "Identifies the asset to which this NFT represents",8 I9 x4 b* O4 T. v+ }9 |, z
        },6 S5 H3 ^. s" L) D
        "description": {
9 o9 t; m: O4 T- X& q            "type": "string",
: y# m/ @1 Y; e! s+ T            "description": "Describes the asset to which this NFT represents",4 L/ A: n" n. m$ {- D8 j6 f+ V0 A  e
        },
" U6 j1 a8 p( x" x7 H        "image": {+ {2 R0 H) q- L- w- Q" z
            "type": "string",4 `# D+ H' `& C, O9 d
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.",7 Q" _5 z8 e# l/ ^$ |
        }
# K5 \  U! t9 f2 |    }
3 V& H8 o( T5 _5 N}( `% r* j8 ]7 G( Y* Z8 J  f  s
tokenURI通常是被web3调用,以便在应用层做相应的查询和展示。4 V% j. W; f6 t7 n1 ~" W
可选实现接口:ERC721Enumerable; Y5 g0 W& i  Q
ERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:
: A0 Z) X0 m# j+ H3 P& f/ W( Hinterface ERC721Enumerable /* is ERC721 */ {0 ?% n9 ]2 x$ a* `) U3 L$ k
    function totalSupply() external view returns (uint256);
+ w, T: |, K! i# A: q    function tokenByIndex(uint256 _index) external view returns (uint256);
& w+ m9 Y/ L- p) W0 {5 e1 X    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
* o8 c. @7 n9 I* K}
/ y4 J( r. q& w( Z9 X# F, Y( C* h) I接口说明:
; C; @$ v6 e, {+ |0 ?0 ctotalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。
- z& Z" r( m9 e5 y% \8 c

3 F) |. \" r" F补充说明- x  J' ?! a1 C$ |9 f( M( @% ~
NTF IDs
9 N; e! o/ e: Y  ~( gNTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:3 G# j% Y4 I# D6 O! m& _
6 j1 s, X2 ?$ i* F+ ^0 P
从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID
. r0 u: G4 z- @( X) f  Z+ b/ S+ y
0 _/ b, r+ T8 d2 S2 _与ERC-20的兼容性
! l7 U  e. H( DERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。4 v/ R8 e2 q- Z1 {, |
交易、挖矿、销毁
# Z4 c$ U4 w6 |; u! W在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。
# O- v  o* ?0 ?/ V/ p1 }同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

有个胖子他姓杨 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    10