Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.: D- Y1 }( ^/ [4 y) q
ERC721是什么% Y; H, d" e% R- @. B) p) U# Q  f
在创建代币一篇,我们讲到过ERC20代币,( r8 V" ^# ^" K2 ]) ^
和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。
- W- k8 K# O$ X& K( G3 Q
+ A$ `" H5 s: s& \ERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。
0 {& |9 u! U6 ]8 n2 M2 T3 T* T

" G5 @, h+ P' V9 M3 b+ j1 ~那怎么理解非同质代币呢?$ R/ P, E* b* K+ v. |0 Z" o
非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。
. `& c+ _: ^. a& P+ O8 i. [; dERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。  l  t3 a3 E5 F' i5 G
* Q8 q, a4 q9 [. o2 G
如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。
5 v* t" {3 S5 D3 G4 L9 _0 f

0 P; U& J& ?; m1 [非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。; T$ k+ w1 U, y5 T# |. R, t
ERC721标准4 J3 \7 r1 R( G: ^! P  z
ERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:
+ K) z" F4 T! V9 `% l; Z( h  c1 `pragma solidity ^0.4.20;( E0 \, Z- M, p9 g( o" |
interface ERC721 /* is ERC165 */ {
6 Y$ c& j# N/ X7 e3 q- I    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
7 f& o: z  T/ c: h    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);- n# i: I" b! w* p, i# d
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
, `' B5 F4 e3 k  x6 D' b6 a8 _    function balanceOf(address _owner) external view returns (uint256);. S" u5 Q+ l; N% \
    function ownerOf(uint256 _tokenId) external view returns (address);
) H' ^! |5 X. h1 w: a4 b   
/ `9 M/ S0 b1 |6 W' D    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
" J1 o/ n% B  z/ w    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
& \( j. S7 q5 c, \! v* u, r    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
, r; ]5 @+ F, c- W/ A   
% N7 I, M# p1 k9 U% ~% [, ~4 f* A  [    function approve(address _approved, uint256 _tokenId) external payable;( p( m3 r$ i2 ~4 g" j
    function setApprovalForAll(address _operator, bool _approved) external;+ {9 a7 z( h9 N% I( p8 {1 u$ C2 R4 A
    function getApproved(uint256 _tokenId) external view returns (address);$ ^$ d$ K0 T6 t) i) {! w/ Y
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
/ B  A5 e$ N* U! x}
5 @' P' J+ q7 d( O  {& W/ Z接口说明:
% z  J- i' ?/ z4 k5 v( f. W* V1 W* q' K. {' M
balanceOf(): 返回由_owner 持有的NFTs的数量。! M" F. e! z& v$ t) v6 J) ^
. f) ^: U- D* Y' N' k
ownerOf(): 返回tokenId代币持有者的地址。( i; M" I7 v+ |) K- s
" |6 ~+ ]& l6 S* X; x1 l! K
approve(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。
7 Q) C! ~, d" E
, F1 O% i2 N$ X2 d$ i* J7 X9 esetApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。* M) m' T1 ?5 n5 E2 M
/ A/ M& K9 J  \& d/ u
getApproved()、isApprovedForAll(): 用来查询授权。
8 e" y4 k) q5 }1 V$ e$ |

5 ~( E( F6 F/ q. S: T5 YsafeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:
, M/ \  P( F6 u+ b  n' [+ L# C0 Z- Y3 M* m7 G/ F: \7 N, W4 L, S
# O0 z( [4 r) H

! `* O1 t. J" f. x5 e8 }( D9 L调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在监测的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。一个可接收NFT的合约必须实现ERC721TokenReceiver接口:
( @+ N1 W1 @; J, F; D6 `8 g3 i) E( d
interface ERC721TokenReceiver {
6 L4 ~8 s- Y, T9 e2 ~: q6 D. C    /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; M& _' I3 l1 W4 R, ^+ @: N& {
    function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);0 r& o# r- k* l% f. y& s1 H
}
2 l7 H7 M9 t0 s$ t' OtransferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。, V+ a% [. ~: X0 @1 Y
' Q) `- @  ?2 c& p
ERC165 标准
1 H# e( f% q3 R% X8 iERC721标准同时要求必须符合ERC165标准 ,其接口如下:
7 S5 O- R% L2 M. |1 n/ P* Y* {& o* w# Ninterface ERC165 {. k; {& L) `4 j& a/ }( q
    function supportsInterface(bytes4 interfaceID) external view returns (bool);" ~) R2 H% {6 h+ G% R7 T
}
1 r, w! B- w: G3 C- FERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。% }- i# H6 i1 k% e: D* v' E
interfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));或ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。
3 w3 }6 N7 l- ^5 G( i: B关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案。
* Y5 X* X" |" S+ N, N! c. r可选实现接口:ERC721Metadata
3 @$ k! A8 y. Q( M# z# zERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。# X$ T& R- B* J9 J( A* X, c
其接口定义如下:9 \+ |8 M% Z$ u: _6 \2 R7 m* r
interface ERC721Metadata /* is ERC721 */ {( F6 [+ y& d6 N$ o0 O5 p1 B
    function name() external pure returns (string _name);
5 f$ W1 s' F4 k1 _    function symbol() external pure returns (string _symbol);
7 V: V% J5 x3 a% _- {" ]1 H    function tokenURI(uint256 _tokenId) external view returns (string);2 J) p* X' b( x6 X8 u7 j, K% ~% C
}& t8 G0 E( W! c) n8 P3 a$ D
接口说明:
4 Q- b: U" y4 H7 o0 d" Lname(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:
: g- ^4 p1 s$ o' D- \9 s( k
1 d+ u0 p' |  j* ?& L: Q; T( }
{
- G$ A; _6 X$ V% \2 Y2 C6 b! r# y    "title": "Asset Metadata",/ c& S% ?8 g: A: t- _2 ^
    "type": "object"," m' d1 v! g- n" f  L* P
    "properties": {- U. y  Y  s; ?, X. f
        "name": {
- q7 R# B$ T0 {: N            "type": "string",4 o9 p0 K" h* T1 p
            "description": "Identifies the asset to which this NFT represents",8 k2 O1 |' h4 j. A
        },& t- i) L1 Q* y  g- F
        "description": {* I6 U  m  y4 x) `* o
            "type": "string",9 _7 D/ X  S3 k" _' _, c5 k
            "description": "Describes the asset to which this NFT represents",; ~& C  J+ M$ v6 ]: a" p
        }," ?( R' ^& j; _" X& @5 {
        "image": {
: A. ^+ P0 H% ]. v5 h            "type": "string",# C/ K, h& B6 {
            "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.",
# y" ~& F1 u8 k& i, w; k, P0 M% q% D        }4 m+ S' Z6 A& \; t4 V2 W) Q
    }' |% f+ A1 o0 r  m7 E, C/ L& V
}
9 m  B% R" a; Z, VtokenURI通常是被web3调用,以便在应用层做相应的查询和展示。
. x: D* z9 ]5 h1 {" i% G1 [可选实现接口:ERC721Enumerable$ J/ {& u5 ^; Z# |6 i2 P" L
ERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:1 z4 s5 Q: H/ M; W' ~9 D- t$ N
interface ERC721Enumerable /* is ERC721 */ {
: Z% O2 J2 o5 U3 b" z* j# a6 R- N4 L    function totalSupply() external view returns (uint256);- r3 }2 b/ J. A' Z5 g
    function tokenByIndex(uint256 _index) external view returns (uint256);
* H+ E+ g9 h2 X/ m- |& T    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
6 o# o+ {2 G& L( u9 W  X/ U% r}& T5 \0 a, B1 M
接口说明:
, z" T4 I. i/ d0 r" h+ @totalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。
% Y& x' I- a" e* Q, s' k' C

5 e# j$ Y2 J/ x4 P! h补充说明
" \  w2 J. ?6 l* N9 r, j, xNTF IDs
8 A, G. I8 M$ UNTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:
3 {7 C) ?+ y1 k8 M2 S* G6 p; [) @! X: I, k$ U7 K/ N% x
从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID
  I2 i% O* p! ^7 v4 {
/ M& B" B& F/ ?+ a! o7 j0 O8 Z与ERC-20的兼容性& ?) m8 S1 H3 \. ^% o
ERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。# i  i4 l- G7 b1 O* q! e  v( o
交易、挖矿、销毁" y9 x8 x. u3 J" ?9 L- \
在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。
/ ]: T: ~" k" C; T1 Y同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    10