Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.% p9 Y& S/ `. }  Q8 l9 a& C7 O
ERC721是什么
1 j% A3 z. L' V在创建代币一篇,我们讲到过ERC20代币,- U* c0 V" K! h  p. m% l
和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。7 Q) ~. M; p' w

1 A! G; }1 K" k5 Z  @ERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。# ?5 s% d) @3 C6 @- \" I1 h
- l9 U! C# O/ f! N3 A" K
那怎么理解非同质代币呢?
5 p* X" ?9 _# J非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。1 j% x: p4 w& M0 ]' b
ERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。; f' T2 a3 W) k3 b  _$ R# R# Q
; Z' ^$ n" J) Y4 X5 O8 {8 i
如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。5 N' N( O: Y4 S" N# T3 f

* r0 L. \5 i8 p0 m# L' Q2 U非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。
' B+ {4 D9 r' dERC721标准
2 j4 E4 C2 N- r5 P3 BERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:! v6 w  r3 b5 u7 ^0 O
pragma solidity ^0.4.20;
6 {7 ?# Z" \: W/ o7 Q% k5 u6 Rinterface ERC721 /* is ERC165 */ {0 _# l6 Q* b  {7 I8 u9 ]: Z. i* b
    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
* q( c; r3 O0 B* G0 t( K. F- o3 E% o    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
$ b! B% Y0 F( V# B  E" G    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);  @% v6 [/ `0 n, ]
    function balanceOf(address _owner) external view returns (uint256);8 U2 p1 X4 s' p  |% d
    function ownerOf(uint256 _tokenId) external view returns (address);) D" i! F0 p8 U* d+ N" O$ J
    , x) [+ i( h" T% o
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;1 @) B& f- n( o$ T7 z
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;" `( s9 Q  }% L
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;! p' h1 \: U/ ?9 r2 G
    ; z- F7 _( q+ L5 W) J$ P# A
    function approve(address _approved, uint256 _tokenId) external payable;& k( ~9 e+ m0 U6 A, V! b
    function setApprovalForAll(address _operator, bool _approved) external;7 _0 k- @) [& u
    function getApproved(uint256 _tokenId) external view returns (address);" N% f( K0 Q  _; C3 _$ J) \
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);, S+ J7 A6 y! O
}# u; i2 i& a% j& b
接口说明:0 j% s2 j5 _' j
8 {9 N1 {1 z( s
balanceOf(): 返回由_owner 持有的NFTs的数量。, {) ]; v- r( h4 S/ B, ^/ \5 x
- f4 r( Y4 Q+ p9 ~7 C7 c1 g
ownerOf(): 返回tokenId代币持有者的地址。1 E+ S# {/ K$ H3 s" ~; P1 t

* l6 H  H! b3 H* l% M. yapprove(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。
0 K5 c+ z* c- \7 [3 f
) I5 e/ Q3 P' m( k3 T  s& [setApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。% d$ S+ F% v% ^0 {5 i. e# ?6 K
, `# ?/ y* k# f) B! L
getApproved()、isApprovedForAll(): 用来查询授权。
4 o5 O, x8 U: q8 n# A  V( m

% s4 y0 \- G) r, ^# SsafeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:7 y3 G* N$ x; K6 D; [

5 G7 x3 T+ @1 E6 h& u  T% P, h
- Z+ c$ N/ E0 @# F; f& M

/ F; s3 ~* X/ G6 h调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在监测的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。一个可接收NFT的合约必须实现ERC721TokenReceiver接口:% P+ E: x5 P5 b7 e5 p: f  Q- C

6 o- g# l2 T& n+ y+ }interface ERC721TokenReceiver {
( k3 z9 @# H2 v! x. [    /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`/ C  I- T1 {( Q) t
    function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);
- Z9 F7 B- w9 T, j& L4 s5 l9 V}, L0 y, L$ y% G: z, S
transferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。; r, p1 J$ Q2 d; D: b

, J1 q& N. {& U$ E+ @5 a; hERC165 标准
$ V9 Q3 U$ A3 mERC721标准同时要求必须符合ERC165标准 ,其接口如下:& r& J3 S' s- B6 s" ^" m- _
interface ERC165 {( k# t. ?6 X" M
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
# d3 {$ t5 f4 u/ r: G1 H" i" C}
' T: W+ Y% U- }% t- ~2 U" ]ERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。4 Y' b: J' \. h
interfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));或ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。# N7 d7 I, Q" x. N' f+ z' {
关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案。+ j$ d; H: X& e" O6 C+ y0 y
可选实现接口:ERC721Metadata
, ?4 W1 D1 E- [- Q) SERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。% m8 N' D) d/ u" R7 H; ~) W
其接口定义如下:' L7 f8 M+ ?( `8 Z) b, C
interface ERC721Metadata /* is ERC721 */ {3 x+ g% N9 p! ?* F$ H) M
    function name() external pure returns (string _name);
+ j0 Q2 P  u% M0 R, p% \    function symbol() external pure returns (string _symbol);
# A7 v: B9 u" p' v    function tokenURI(uint256 _tokenId) external view returns (string);4 @9 Z# Q- P0 v* u( V
}9 p3 s$ G& m8 b: G* X7 c
接口说明:4 M& D& n7 Q% ]0 i
name(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:+ i, l% g- P0 S  [

+ f6 A( w. O  H, T8 M1 j4 l{
. _5 r9 @$ u1 R3 Y9 f    "title": "Asset Metadata",
4 d$ h" i- [# q' t! t% y& w2 \    "type": "object",9 s4 a! ~& z2 H) V+ j
    "properties": {
2 F; x3 H' R* ^9 `  I        "name": {/ v- R, [, \6 j1 D% e3 J1 Z
            "type": "string",8 Q7 A9 F8 @9 }/ j( m
            "description": "Identifies the asset to which this NFT represents",
. _$ J! X/ [5 r7 ~( |        },
) e7 H7 r) G. y0 z7 N        "description": {
$ q( M- C$ X4 {- X            "type": "string",
: h4 Q1 p8 n& D& ~: [" h5 T! L: Y: x1 ^            "description": "Describes the asset to which this NFT represents",  p4 w1 ?  `& S: O. m' x0 ?% z
        },( r; Y. V$ K) l( g- w6 E% @
        "image": {
6 f0 m! ^3 @2 Q  g" w/ ^. {            "type": "string",
, H3 y; I6 R/ B% ]. i/ b6 k            "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.",
+ V5 {3 `: G" ]' G        }! @7 D5 n$ |1 a( g
    }% _& h& x/ T5 a, Q6 K/ L: ]
}0 T* B9 |# X; P9 E. s$ K# H) E- {( c
tokenURI通常是被web3调用,以便在应用层做相应的查询和展示。7 c. p! A/ Z( m+ R, g
可选实现接口:ERC721Enumerable# \. i& }4 v5 N% a& S' M% h6 p
ERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:
6 e" ?7 g) n4 P4 d! Ointerface ERC721Enumerable /* is ERC721 */ {$ c4 c% f2 U; C4 \- d9 V( e
    function totalSupply() external view returns (uint256);7 G6 n# N  a6 _$ N& ^; d
    function tokenByIndex(uint256 _index) external view returns (uint256);2 v  M8 j3 ^1 V
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);+ C1 H8 @' ]. [/ K' t  i
}
) n8 U& G( l+ r2 ?9 ~接口说明:
& H( F" W0 @' f& r5 VtotalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。
/ a- @" S/ m1 k$ o! m

  V4 S% X! K. m; D补充说明5 y3 L3 I, n  x7 L9 X" {- @
NTF IDs0 u' L5 X9 p8 f2 _' E8 f( h
NTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:
8 k; ]- z: D' s* z5 I8 I
0 e' z. t' w" H3 I/ t; l0 h/ ?从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID; J1 K0 m9 A8 b, t- d) C

( b8 O8 _* Z; l0 L* U4 {, g' _% T与ERC-20的兼容性
& k! ]: Z# b/ S0 D. z6 x* ^+ BERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。2 ?, x' p$ Y/ P8 e2 D- l
交易、挖矿、销毁
1 I1 u, r* r' n# j# y3 ~在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。# x; A& w: ^+ j  j& R7 S
同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    10