Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.: o& H0 y' H( N" `
ERC721是什么
" v- K  p4 {# @在创建代币一篇,我们讲到过ERC20代币,! }! T& |& h# Y8 ?
和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。
( M+ o( A+ _% I- J1 F
  S: L& X% c5 a: `) TERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。/ e" R" O8 n) `/ i: ?5 ~' z
& w5 N* Q6 J. O: p0 `2 z. h
那怎么理解非同质代币呢?
5 U7 e. r  D. a非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。
' [' X& D3 \: S% k- |0 f, FERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。
) [1 B( O: O7 F7 c  B
! X0 e% }1 T3 P+ Z( S如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。
# y# \3 d7 F: X+ t

) u' L' N  o; Q( N% B) b非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。/ S, `) E) m( i3 S: G; J
ERC721标准# Q6 r; Y8 V8 c, h5 k% |2 p
ERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:% t3 s& A; E3 ?% Q1 }+ \$ M
pragma solidity ^0.4.20;
) |6 k, W; Z, E" ?/ R: c, V& p0 Jinterface ERC721 /* is ERC165 */ {
$ T- W& F+ J+ }0 c2 ~0 H    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
  k0 N' j+ h) p- N6 P    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);6 w* f5 J2 g5 A- R2 a! \1 r+ h3 k9 Y0 `
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
) m, G0 Z  d! B2 M# @- [  Y    function balanceOf(address _owner) external view returns (uint256);
$ N+ Z1 t% \& L% C    function ownerOf(uint256 _tokenId) external view returns (address);7 b* g- \, n. Z. J; B
    & g( M4 i; q4 I  D9 F5 X
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;% M( D; i9 M5 }8 z
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;: E  W  M) x) O( v; B9 m
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;1 p6 x) t  P! O
   
7 @2 F8 y: e9 C- E0 ^* S    function approve(address _approved, uint256 _tokenId) external payable;
0 V" A5 e& q; v" Y    function setApprovalForAll(address _operator, bool _approved) external;, }' i+ _" R" G5 ]8 L
    function getApproved(uint256 _tokenId) external view returns (address);
' Z* ?7 ?2 L7 Z3 a* l- i    function isApprovedForAll(address _owner, address _operator) external view returns (bool);0 e* F7 S$ J- A3 [1 I
}
/ z2 N; }+ g" N* S接口说明:* O* a/ _; v; O& S' M* o

  A9 O. [& h- {balanceOf(): 返回由_owner 持有的NFTs的数量。3 U! f: U& D7 l$ T+ Y
+ j7 T6 L! f( M6 g: q0 B( h% g
ownerOf(): 返回tokenId代币持有者的地址。
* m1 C. o  v' `1 u1 n" N: q) R! {  a% u
approve(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。, J/ J0 X, ~8 l0 B5 F
* l" A$ b4 o; [4 a
setApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。  o) i/ O' i- u% K6 \: T* ]- P
6 j# D& d' D5 b9 [8 x6 w; ^
getApproved()、isApprovedForAll(): 用来查询授权。! X/ B2 J+ ]  U9 l3 F

. V6 M: B" l$ E+ K2 v; C, KsafeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:
6 Y/ v: _4 R4 m8 e3 |7 J0 j0 D0 d
  g- M$ V# a- R4 o( i

+ a/ {, U3 d' P2 |& R0 n
6 G5 R. I+ b! E, H0 M1 d调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在监测的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。一个可接收NFT的合约必须实现ERC721TokenReceiver接口:, V. C9 W6 C/ N! w1 n
  M" n! _4 m/ h  k
interface ERC721TokenReceiver {4 K6 _3 @4 E3 {8 k( \4 r/ M2 J
    /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
- v4 C! \  _/ ]. A2 M9 e9 w% W    function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);9 \  T# l  d' z/ u/ _6 d" J7 v
}4 F* L  C1 ]: z& P3 Y
transferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。, [9 `4 C' ]+ q3 k/ @/ m- n% \1 j

5 E/ |/ v9 U' L4 c' y9 a6 ~ERC165 标准
& H7 _! s/ @2 _ERC721标准同时要求必须符合ERC165标准 ,其接口如下:
# V, j  r: ~6 H& winterface ERC165 {
1 p2 t2 y7 W9 b4 I) O- f! j    function supportsInterface(bytes4 interfaceID) external view returns (bool);
$ F4 a. I5 B0 k5 t}
' R$ Y5 @7 U* R& P4 kERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。
, r& y" _2 p, f$ C* k. W; N5 HinterfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));或ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。( j) c% {: I3 v
关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案。
5 I2 y4 f# P+ W: ^0 _可选实现接口:ERC721Metadata, k, a% O, n& q: C) E
ERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。
* I+ }1 S1 |, F' z2 s! a其接口定义如下:& O  T5 Y+ `+ f" |
interface ERC721Metadata /* is ERC721 */ {5 `! `& {$ O0 }: y- z
    function name() external pure returns (string _name);
: l7 E7 w' a0 f# O    function symbol() external pure returns (string _symbol);
5 f6 c2 E* a, W) z    function tokenURI(uint256 _tokenId) external view returns (string);
+ H9 J) B# V$ c}
3 Y9 H7 N4 I; J# a( X8 H% h接口说明:
& i) J+ b4 g7 B6 u" H7 C3 Q" `  Cname(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:
, Z7 F( }2 d8 U* A

* z2 ?; o1 t- N9 [1 e{3 D2 M' Y8 @6 O
    "title": "Asset Metadata",
! a* K4 k% x: v8 G5 y8 r    "type": "object",( d. p/ ], ]1 e1 d/ d9 ~
    "properties": {7 C2 t! m% t1 P1 V* E$ W
        "name": {* M3 `- \9 B, Z0 W% i
            "type": "string",
) h% I9 @4 d% o3 z            "description": "Identifies the asset to which this NFT represents",
* `9 E4 [7 e* M* ?0 `5 E1 U        },: |# ~1 B( v+ L/ ]$ w
        "description": {( o1 ]' B, p' Z3 L/ g  y
            "type": "string",
9 o8 c5 ~# A: M$ _# ^3 U            "description": "Describes the asset to which this NFT represents",
8 g  X$ n  T  s! r! q5 T3 ]        },) v2 T2 ?& R2 J; G) N0 V
        "image": {: G3 H- p" b. o2 I( q
            "type": "string",
% o( ?0 w& y; }- k9 A            "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.",$ Z+ e8 V3 T3 p8 H6 N' }3 ]( E
        }7 h0 d5 V0 Y# x
    }
9 G; i* r6 ?# R  I: v2 M! g}% F' K1 `7 J7 X5 x; I0 z
tokenURI通常是被web3调用,以便在应用层做相应的查询和展示。' n% {) Q; K/ V" H' V, _
可选实现接口:ERC721Enumerable
. I  C! i, _* i5 o, v, \+ _7 uERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:
( ]9 m/ u6 O0 l9 Q, _6 linterface ERC721Enumerable /* is ERC721 */ {/ O5 k, I8 d# }' L2 c2 [/ r
    function totalSupply() external view returns (uint256);
( \# D! s/ g2 B1 R! {/ {    function tokenByIndex(uint256 _index) external view returns (uint256);
0 ^" `" r7 y$ }3 Z, ~. a    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);: R  o- x4 n3 F& Q1 Z& J: k! F
}
& Z" {- B' D- T  t接口说明:+ ?1 u+ `$ l8 Q# d3 W$ v
totalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。
! |) G# s+ r# H* b. `

/ |; y: n) v; n3 A$ T4 a补充说明: Y% y+ B7 f- ^$ Z5 `
NTF IDs" z' H( g2 J+ C4 @
NTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:
7 `+ w; s& p5 x' Q' ^
& }# c0 w0 n9 z5 C/ c从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID& m! Z; }0 ^' J7 E9 Y5 p1 x

1 h4 Z( ]5 K) H$ k# K" h9 G与ERC-20的兼容性+ q4 T% {6 B( s' x- ?
ERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。
2 J: T- \$ `" g8 ]8 W交易、挖矿、销毁3 o& ~# y; G" ~% a
在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。" r* v, a# k7 C
同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    10