Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.
$ E6 k* j/ J: w7 m* ^$ Y5 P: B! IERC721是什么( Z5 E8 }1 p# }# @
在创建代币一篇,我们讲到过ERC20代币,1 L, i; ]6 C' S/ j) R0 J( w! }1 w
和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。
, z5 K. _* Q5 e2 ]9 D( w1 z, ~9 U- w  x! v7 M; {
ERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。
1 O: U: l' Z3 I1 X# M1 J

9 d9 C1 H0 k, W7 Q0 |* E那怎么理解非同质代币呢?
$ n3 D( s# p8 n; u! i3 q0 _非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。# d/ l; G$ `! Z! j4 U# Z
ERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。
% o, z- R0 ~. v2 l
/ q4 F5 ?3 G$ u" n如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。
1 \9 G) l0 V6 N) A
$ a4 C( V4 Z1 d) M% L, {8 [8 x
非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。8 k. S' X6 j8 g
ERC721标准
& H6 Q2 n4 {' }& iERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:
% s* G3 O& j+ R/ L/ K  fpragma solidity ^0.4.20;
2 ^& I( |# a4 t% Pinterface ERC721 /* is ERC165 */ {% t) G) X5 d( ~5 v/ R2 l- V
    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);+ `! R* W) V8 G9 V% W
    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
" N% N3 l, c  w3 _' r    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);: y* ?0 \3 g4 j- L
    function balanceOf(address _owner) external view returns (uint256);5 p& \+ d% [! b, U: ~  j4 f0 R
    function ownerOf(uint256 _tokenId) external view returns (address);% p/ H/ [  U" ?! m& t' \
   
* ^/ z0 c! J; [- `+ J9 [/ z    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
9 d4 W7 @& \8 p# Y1 N* v# N  H1 d    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
3 V* J* p. A7 i% {" a1 m0 w    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
8 o0 e9 r& S+ M& `  l) [. x% f3 P    . F: S+ W6 C' L1 k, b
    function approve(address _approved, uint256 _tokenId) external payable;1 x( {! l5 v- n% _/ V9 t+ Y
    function setApprovalForAll(address _operator, bool _approved) external;) w4 D* q) ~' @6 ?/ T* ~$ J- m
    function getApproved(uint256 _tokenId) external view returns (address);
; F/ }5 _: C- n- b% G    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
- m  l- g! h: r7 T9 M4 l% N}9 ]( d1 @) n) ]# |. m
接口说明:
; [8 o8 V& Z3 n7 J" f* I7 {* l# D
balanceOf(): 返回由_owner 持有的NFTs的数量。
, U- ^/ m3 P2 b$ S3 v3 u1 z
/ D) ^" p# W+ X0 }" QownerOf(): 返回tokenId代币持有者的地址。
/ y# K. u8 Q  a$ C( ]; V1 F% D7 A/ Y/ d4 R- U& X( _) O
approve(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。1 e" n2 B- `9 @
0 q8 d8 D6 w' L9 Q* S) O
setApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。6 r" \: _: e( q. A5 C; T$ ?! M: S+ R
2 c0 q  u2 t9 z
getApproved()、isApprovedForAll(): 用来查询授权。) q# U1 Q( f+ ~1 d  t; w# c5 @3 U
  H& s6 R5 A: [0 v
safeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:
; C7 y& ^6 @$ A9 j, a! N0 m& F% z" X/ y+ U7 z4 J& n# E, S
* d1 T5 L7 q% p5 h: j& F

4 u3 K& w9 Z( D7 b& L; E调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在监测的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。一个可接收NFT的合约必须实现ERC721TokenReceiver接口:9 ]: D; ~! s- |: |# L4 [+ o

' k- `* J4 W: ^; {) F; W4 Einterface ERC721TokenReceiver {
; @: c# ~$ O' ?7 X    /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
# J, [/ Z) C0 G& {# ^0 L    function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);! x( U7 @1 n3 k. s4 k6 G
}3 `5 o; k7 a" {$ I/ R! @3 T
transferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。
; {) s- ^7 I: n  b; V  ~' i
+ n' F- Y7 S' m+ P' K0 W6 }
ERC165 标准/ D$ o- A$ T) n4 r7 P
ERC721标准同时要求必须符合ERC165标准 ,其接口如下:
. K2 n( K( X' a9 sinterface ERC165 {
" {# T/ t4 m' E- D. ]$ k    function supportsInterface(bytes4 interfaceID) external view returns (bool);
2 z: z. ~0 I, E6 L8 P& O9 s- r}. D# C% Q7 t$ `  S, H/ v7 e% ~
ERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。
1 t8 ^  N  e2 ]; TinterfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));或ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。8 q7 ^' E% N" i! O+ C7 ]3 Q! c
关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案。
* x9 p4 M2 I8 g5 A1 Q9 C3 R可选实现接口:ERC721Metadata6 v- n: x% \: C* ]
ERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。
# j# V) g  W& Q: V5 D7 }% P/ z其接口定义如下:
# M+ W2 j# P( h; Y. Binterface ERC721Metadata /* is ERC721 */ {
- I5 O& d$ j2 n: Q  v0 I' o    function name() external pure returns (string _name);
, l2 }* h1 k, b" a2 r9 E' ?% _    function symbol() external pure returns (string _symbol);
+ H& u8 Z" U1 u, s6 `; X    function tokenURI(uint256 _tokenId) external view returns (string);
$ j( K+ f1 x# Z9 K}
. \3 R% V' M1 g0 H" Y2 e1 E接口说明:0 A& W0 \0 h: `, Y& `; N2 |
name(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:3 E0 O9 K4 k) r+ k  G; i

  L4 P4 G7 |' C4 u{
  d4 c0 Z# k: q/ e4 u* b* k( B& R    "title": "Asset Metadata",/ z$ o3 l; E/ s/ @" M, w
    "type": "object",: r  z# r" T5 k2 U. O
    "properties": {0 i! A1 r% b% n/ v+ B" \
        "name": {
3 i# s2 e% Z4 \; ?6 T            "type": "string",0 _4 L) F$ {% t  k$ F* Y
            "description": "Identifies the asset to which this NFT represents",
+ J( h) ^: g1 @" M) }        },. ?2 w: m2 [) r
        "description": {
8 i: C5 R6 ]$ \$ O            "type": "string",
: Y3 a5 B0 P6 M6 n. e4 C! y            "description": "Describes the asset to which this NFT represents",
" i0 X* l6 p- ~% N) Y        },
% u0 Z- C3 ]3 \! `# ~" R: ]        "image": {
( Q/ `- G9 |. P+ |" y' P            "type": "string",
9 p) j( Y) \7 P* o9 l            "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.",0 `' h' }) [* W7 e0 x
        }' Y: K) G+ p2 Q, P6 o# d
    }
3 n/ @. B9 F3 D" j  `6 ]}9 @# p! a0 u" [& G; v% f# \
tokenURI通常是被web3调用,以便在应用层做相应的查询和展示。7 W1 F% ~3 m) v: L3 F
可选实现接口:ERC721Enumerable! _& q0 z- o* U, c
ERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:+ F9 @% y; Z" y# K) [
interface ERC721Enumerable /* is ERC721 */ {. r% e3 B3 i) L- j$ e* a
    function totalSupply() external view returns (uint256);
' X$ t7 l4 j# t; H, k    function tokenByIndex(uint256 _index) external view returns (uint256);
: h) |- e1 _4 E- V8 V    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
1 h8 o% ?5 X; L  Z5 Y}. }$ `6 D( m* e9 x1 A7 ?$ H7 f/ y  P1 S
接口说明:
& Y) L- \' d1 L3 w  C6 ?) [. ltotalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。3 b* ?% U0 W& n! p. E: j) Z

) L: t/ N# Y2 ], H补充说明
/ r7 k; Y& ]( X  i9 R: ENTF IDs
1 `. w: H' d- H+ V% f9 y; Z+ dNTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:
5 f- N( z9 m- }! G2 C- V% J# G- S! O3 X3 H5 ?- @
从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID
6 d5 `9 F$ V2 j( n& P; I" G. D. T1 N6 d: n1 G
与ERC-20的兼容性# O2 Q9 b: @- m1 e/ H3 g4 W
ERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。6 v, T" l7 b; X( l+ }: K
交易、挖矿、销毁9 R. x; Y" t2 i8 T0 H$ B7 e: S
在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。
) X- e, r+ T5 a0 _+ L! u# @3 g6 W4 \同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    10