Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.
2 M- g' u& y  K1 }ERC721是什么
9 t6 G/ s3 ?% M2 |; W在创建代币一篇,我们讲到过ERC20代币,
2 [6 r6 O3 q( q) f: U  E9 H和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。1 ^# n1 h8 |( e
$ R& d. d% ]4 I/ M* s! k" Z7 i) ~
ERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。
. l" L. q1 S0 Q/ [. [+ D
) b8 l/ U  ?8 P4 J4 u7 G1 A) b: p
那怎么理解非同质代币呢?
2 ^' m: G4 P6 J" g6 ^: ]1 |( k非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。
$ O* r8 U0 U8 k( tERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。6 m# p' R1 E% o% w) s# i- S
6 U; z' s% g: R+ a0 v2 l
如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。2 c! L; G3 i) m$ b
" d* ]1 d4 j$ P, L
非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。
& ~4 L. Q2 m/ i! W0 e1 SERC721标准) U* w7 d. J  Y
ERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:# p5 F: E5 m: ~& x
pragma solidity ^0.4.20;
  w8 ~- [% m1 b6 @1 T% Xinterface ERC721 /* is ERC165 */ {
" h' E4 }9 x( `( T7 v6 l    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
9 b5 B9 f4 \! z# ~    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);4 |% b1 D& C% ?( G8 V- ^- I
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
+ P. K4 ?, M; L- h: u0 ?    function balanceOf(address _owner) external view returns (uint256);
* a( g7 {, h* l; Q    function ownerOf(uint256 _tokenId) external view returns (address);
; R2 f# u: _; S6 U   
# c' }* w, F: p+ \; d0 R1 c6 n    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
0 e2 U$ F, ?" t! O    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;# w. i# c7 W' }' m! ?7 X/ z% r
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
: u. a, w! s2 v- `$ \3 x! `: O  I    " T8 ^5 S2 C: ?0 J- U
    function approve(address _approved, uint256 _tokenId) external payable;/ l- _( S: `: ^8 o" X/ O
    function setApprovalForAll(address _operator, bool _approved) external;
) `; L7 {9 o- Z3 A, [3 o  I$ K. g9 T    function getApproved(uint256 _tokenId) external view returns (address);! f" {( t1 w( j% Q8 q
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
0 K! L: H7 }' B7 i8 `}( u$ Y+ H) P1 p, c8 P
接口说明:
& l! h7 k& Y# y; j1 h
5 a; q: N) ^  K8 S$ \% }9 i; M% @balanceOf(): 返回由_owner 持有的NFTs的数量。
7 Y9 I& r7 M; P! o, G1 g7 d) @$ R7 `9 z4 r0 X
ownerOf(): 返回tokenId代币持有者的地址。9 [/ n$ }: E( @7 b% [
$ K- c+ V# R1 g& D
approve(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。" K$ H- S! N2 ?' [3 |& I

: C0 y, ?. y) YsetApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。. c4 i$ J+ D* s

5 F5 [7 U  ]% l3 U3 m# QgetApproved()、isApprovedForAll(): 用来查询授权。0 E! p6 M# r8 x9 N, ?" G) y

& ]+ F2 Y) _4 d& j% F0 {' m' U9 [safeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:
% `3 i' n9 k( O" Z7 Z! T1 i
5 {3 X3 x/ a; R
4 c" E0 B6 {' v# N7 a- Z! {
$ |! a. u. b0 B+ O$ L
调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在监测的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。一个可接收NFT的合约必须实现ERC721TokenReceiver接口:
* Y- x- S/ Q/ ]# U! ^+ W5 u8 o, q8 _6 i! c
interface ERC721TokenReceiver {
5 L! I& h" c9 p    /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
: E4 v7 n  n) X# n8 l    function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);
9 N$ G0 e) Y6 f; T, y3 b1 ]) e}
$ I2 w7 k8 H6 G5 M& }! QtransferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。
. O! ~% l9 p/ _! s
8 t* Z4 p2 {/ M5 x! L3 {# w
ERC165 标准
9 d* m1 |4 ]5 U' W: YERC721标准同时要求必须符合ERC165标准 ,其接口如下:
% w; p. Y( f& R/ \" Kinterface ERC165 {
' a% Z) y7 ~  \    function supportsInterface(bytes4 interfaceID) external view returns (bool);6 s3 \9 {8 d+ q& j2 q% Q
}9 f1 u; V# j8 {2 R. w: e+ o2 g0 K; W
ERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。4 |( A% H7 R9 x: p8 Y5 ~5 C
interfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));或ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。
, Y/ I- o7 _$ B! G7 {9 O! f关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案。# ~+ A9 k' s0 o5 g  q& j7 \9 H
可选实现接口:ERC721Metadata! F0 ^% p/ s) g- L/ f
ERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。7 `& T! M4 T& ?5 G: U
其接口定义如下:
* m7 q4 y; u/ r; G8 |  Ninterface ERC721Metadata /* is ERC721 */ {9 V) J0 ]% Y1 |
    function name() external pure returns (string _name);
; d* d1 D: X: b    function symbol() external pure returns (string _symbol);$ N; h) |# C/ ?. x. R. @$ G
    function tokenURI(uint256 _tokenId) external view returns (string);. ?/ B" S' U7 L9 @% t
}9 k: I/ @3 S! q9 U0 y* {. w$ j
接口说明:
) T, l" U! Q8 B, z, m! m$ j1 tname(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:$ p0 B' U. \) M+ y$ _6 a
6 z! G* B1 \; U! s2 V8 [
{0 M& `/ r+ u/ o- g2 r
    "title": "Asset Metadata",7 G$ F3 b+ E. M- d& O% @+ j" T: V
    "type": "object",
# N& }0 z& b/ W  F4 D    "properties": {) [0 [0 ^/ E; E
        "name": {
5 L& u. K7 F8 F0 ?' S& v            "type": "string",; O3 S$ c8 P' F" @+ ]- F
            "description": "Identifies the asset to which this NFT represents",
4 C( T! V9 R& V3 Y, g% l8 z  R        },$ n0 Y' n. u) {8 `. X* U
        "description": {7 ~+ O6 V+ i8 p
            "type": "string",
  `% ]' Z+ I/ X, x& Y! M/ N            "description": "Describes the asset to which this NFT represents",
  P! |; s6 I! t        },: r, Y6 j8 Z# x) B
        "image": {% R. [  [3 S1 l2 S1 \# l
            "type": "string",& y. K7 O7 v+ j, _: h' 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.",! U* g2 n: g1 y- z
        }2 M! I) n: _( ~6 c# c0 q% ?  v8 g
    }
% P1 {+ J0 s$ n5 q9 C}
6 h, O4 t& E; `* ?tokenURI通常是被web3调用,以便在应用层做相应的查询和展示。/ o1 t& m4 |1 b9 g6 v
可选实现接口:ERC721Enumerable
) H  \. Z; S( e3 Y9 KERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:
& J5 y* o" K; q# ?interface ERC721Enumerable /* is ERC721 */ {2 s) ]1 E# S4 a* I! H
    function totalSupply() external view returns (uint256);+ G6 u7 _5 F# l8 y- h
    function tokenByIndex(uint256 _index) external view returns (uint256);
# m* y; T! h2 f* F    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
- H5 ]/ E! T9 v) Y9 E) t}7 l- N9 M/ z& D5 S5 Z
接口说明:
' d7 ?3 S7 B  m; ^/ gtotalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。
: h8 w! {1 n0 @; X+ M
' G  V8 [* i7 Q, n2 d  k; X5 C
补充说明
" N7 o, D( Y0 O( CNTF IDs  C/ V1 I7 F; q9 h) g
NTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:
) Q- z- ?3 B% K) W
. e8 \9 R  a+ W从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID
+ p/ ^5 i2 a8 g1 _( X; `) s! w. r2 E& N  W$ K8 r3 b7 O
与ERC-20的兼容性# {6 p1 F  R' o, \) X
ERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。/ G- k1 f* w4 D* f2 Y1 G9 L
交易、挖矿、销毁
% j6 M/ ^3 P+ e% J" X在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。. B" H3 ?6 z! i
同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    10