Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.
( Z- A1 K& Z8 ?ERC721是什么  ]5 A) o2 X1 W  V
在创建代币一篇,我们讲到过ERC20代币,0 y% m% w( ^6 O! u  L; N
和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。
% M  t) _8 b7 L
0 y# S  q/ N7 `6 P( O8 {5 \8 SERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。  q" F5 E& U. @. ~

# @1 R3 m$ x! o5 E% Y" K$ a2 c/ \那怎么理解非同质代币呢?/ `9 Z$ Y& D  u' \! X1 Q) P1 z
非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。
2 Z9 n( |  R& q, V( ]5 XERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。8 H- b, L! P- I
& z2 |1 n' q+ }+ O3 J' c5 D# S. e
如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。% [3 T7 j( u+ L# R) w. Y2 f: a3 q
0 E. J% C, e6 K3 Q* W
非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。  s& O1 t6 H0 v+ n) D9 c: n  u5 _
ERC721标准/ c, o4 @: x+ f0 h; f
ERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:
1 T( J6 d7 ^( I  F$ ?* [" s! ipragma solidity ^0.4.20;/ G" u; G# d* R; E( P
interface ERC721 /* is ERC165 */ {# c$ D5 }2 _6 v4 e/ S  u8 k6 ]
    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
! g( f. _( A' X/ ^0 O    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
: G  O' O( v# Z/ _9 |9 T    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);: X, u* |% z+ S, |7 g! l
    function balanceOf(address _owner) external view returns (uint256);
; Y( y$ M4 _6 g3 S    function ownerOf(uint256 _tokenId) external view returns (address);
9 R5 R: a. q/ }" O" F0 U  `' D( K/ o   
) u6 r" b8 T1 Q# b    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
! c' [" m8 T; C& [% U7 r# q8 s7 t    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;7 B. _. o; M+ x, H, a
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
4 l7 `8 u4 s4 N    4 F5 R5 m% N4 G/ {& X8 P0 a
    function approve(address _approved, uint256 _tokenId) external payable;7 u* T( A" C3 k: s) [/ d. J# W
    function setApprovalForAll(address _operator, bool _approved) external;8 m0 q8 \2 F& N
    function getApproved(uint256 _tokenId) external view returns (address);( V8 x: b* y; r1 G5 u, L  A
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
- ~: ^+ z8 B' W9 r: Y* T: [1 Q}
" E2 z& g4 \1 W3 k5 q0 p0 m接口说明:
4 x2 Y4 s! D; \! x% L  Y3 E' O/ L7 s+ v
balanceOf(): 返回由_owner 持有的NFTs的数量。4 ^4 \) u$ g! d$ q/ L2 z

/ p! a0 m2 M! e  qownerOf(): 返回tokenId代币持有者的地址。$ F6 v7 d& R' ?1 U1 n% N
0 t1 J  I/ |7 H9 ?* g1 }9 ^/ j
approve(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。$ w7 |8 Q' ?) |/ s9 d. P
/ d  |6 K( O1 d3 {1 C/ D
setApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。
; j3 }; P3 T; N/ {( I
1 ^, U  \7 K9 J) {) B0 [( L1 Z  _getApproved()、isApprovedForAll(): 用来查询授权。* P! U; X' M2 m; v, K

# y( T- Q( w1 W' ]safeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:
8 [' J, \% ?3 g4 Q7 G- E% k+ j- t! j7 V; m0 E

( y/ ]4 P+ c) h/ a
! o4 d: s# O' N调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在监测的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。一个可接收NFT的合约必须实现ERC721TokenReceiver接口:4 x/ ~4 b# o- S9 q( L! h/ |
* l! X; `  j2 m8 ?- F
interface ERC721TokenReceiver {: z5 h* z& r8 d3 @3 D
    /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
+ q6 u  y! u( `8 ?/ ^9 X    function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);, Q7 k  Y& T# u$ l* ~4 h
}5 |" q" T- Y" w' C4 m) Q2 l) L
transferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。
2 R2 Q( z8 k5 P0 Z' S8 |% h- ^
# j  W- P/ C( i% N) d* [& G
ERC165 标准
! y: R8 s9 T& l+ ~0 C" OERC721标准同时要求必须符合ERC165标准 ,其接口如下:
% S; w3 g( Y3 A. ginterface ERC165 {2 _2 M+ r6 A( L
    function supportsInterface(bytes4 interfaceID) external view returns (bool);7 b/ L2 D' W3 z8 P! v  F5 c8 `/ x
}% l6 A) p9 s- I8 O# S; v
ERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。: I" C6 O& B$ r
interfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));或ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。; v+ r6 i! O1 u; D# L; Y2 ?. v: D
关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案。
: D1 _7 |5 C6 l9 r) q  I' k可选实现接口:ERC721Metadata: w% H4 g: o! \, P1 w( f  f
ERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。" O. a( ?# h" R* w1 H
其接口定义如下:
8 K- m( M% J5 @1 [4 ninterface ERC721Metadata /* is ERC721 */ {
0 V/ V2 a* ^, d( E8 C  c+ R2 D. r    function name() external pure returns (string _name);' Q4 L. ^6 R+ y. z4 `; i
    function symbol() external pure returns (string _symbol);
" V) u9 s" u& [6 s2 F* b1 }6 t    function tokenURI(uint256 _tokenId) external view returns (string);
) ?8 G/ J) j9 X( r; E! D( j}
1 B; u/ J; G" J9 a6 z接口说明:: S; i3 S& y9 M
name(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:6 }& r4 l9 \9 G% a" E4 L
5 W- Z. A5 S0 ?, |6 n# Q. f8 u2 F
{2 r7 b2 Q+ V6 [* _. `: A2 E; t
    "title": "Asset Metadata",* q. f) R" X, C6 \, W5 ^/ V8 j
    "type": "object",
! n$ d7 e# ?" R* F1 M3 h    "properties": {( j4 I0 x$ I. A0 C
        "name": {2 k9 o  p# L* [, n! b7 A: U* k4 c
            "type": "string",/ K2 i( n. O* C
            "description": "Identifies the asset to which this NFT represents",
$ F* C  f- o  a  R4 l% C' B. G4 m        },% n2 q, Z  c* M1 B: ~- B- h
        "description": {
: g# `9 g1 D, I$ {            "type": "string",8 B9 R2 B4 r$ t: n
            "description": "Describes the asset to which this NFT represents"," o: f! w3 ~& f# H: ?
        },' X" y$ h, n$ Q% e4 |( r
        "image": {
: {( z/ J- X8 L  C% b  Z: o            "type": "string",4 R% N$ ^& a1 O* m' F
            "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.",: t* g$ C# B: K
        }
* D: n3 k' H2 C& f$ j    }
; y# ^& Q" [' x5 X: R! t8 A}( d# W8 W' L- K: ~) J
tokenURI通常是被web3调用,以便在应用层做相应的查询和展示。  k% V9 Z8 a" |2 ?% L5 g* @
可选实现接口:ERC721Enumerable
' p" F0 g3 s* L8 eERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:
' s+ ^; }( ^( o9 b0 Binterface ERC721Enumerable /* is ERC721 */ {) v  z7 m& _7 B! h5 p, F
    function totalSupply() external view returns (uint256);$ P% E4 u! {: F" \( f" N
    function tokenByIndex(uint256 _index) external view returns (uint256);
  D& n# z$ \" ?; t! Z    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);" C  U& y0 X) ^, `' A( K$ K9 G; U1 [- r
}
( y3 O$ ?. Z" A8 \接口说明:
* Z6 X, y+ }  _- S, W' M4 ltotalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。
' T7 @$ Z& p& D2 I

% M  B% c  [2 p0 Q. j8 ~补充说明/ W* M' D; V0 |' k$ a
NTF IDs; b% w  u9 x4 p" }9 c
NTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:7 `0 @, [) n1 W2 W# Z3 t0 G* R

8 J0 [5 o$ a( a, `, e从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID' _$ L" l& O! H: m6 A7 w

0 n! z" T) [9 X; w- b与ERC-20的兼容性3 g' B' u6 B, Q' R% t
ERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。7 T5 ^# c# s0 ^
交易、挖矿、销毁
& \( D, Y* _) @, t在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。4 C# `. q1 Q( X5 e  k! k
同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    10