Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC-721创造出来的,每只都是一个独一无二的ERC-721代币,不过ERC-721在区块链世界远不止猫猫狗狗,它更大的想象空间在于将物理世界的资产映射到区块链上。本文就来剖析下什么是ERC721.7 ~6 Y9 M8 _/ |8 Y# A
ERC721是什么* O. G* h3 o$ y: O% j
在创建代币一篇,我们讲到过ERC20代币,( G, \3 S: |: A* w
和ERC20一样,ERC721同样是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFTs,多翻译为非同质代币。
. v& t' ~) w5 A% ?7 B
: ?0 ?1 H" m! Q! e# `ERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(2018/03/23官方提议。
% N% l) g! Z2 n% ~2 F" m
' O. m* S9 B! \, N" j/ f6 m
那怎么理解非同质代币呢?
5 p$ Z" S2 `4 I  ^非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能置换的。这种独特性使得某些稀有猫具有收藏价值,也因此受到追捧。
7 {6 Y5 b7 d" e5 s" j# N) bERC20代币是可置换的,且可细分为N份(1 = 10 * 0.1), 而ERC721的Token最小的单位为1,无法再分割。
, ^& f! i  t- D, i
, }# V) s: p; z! }" S* Y0 V  T如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的,而同质是某个部分或数量可以被另一个同等部分或数量所代替。; P  ~3 i1 w7 r9 }/ x! H

: a( M3 H0 d: Q/ F- ?非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。
2 z% n8 D& [* s" p3 Q- _' ZERC721标准# j, M& l6 x  p- w7 Z
ERC721最为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:
" Q( y- P* Q! Y* ipragma solidity ^0.4.20;0 U( K  v2 k# B
interface ERC721 /* is ERC165 */ {
! q) b% N1 O: p: \/ a  C% m    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
- `9 e3 x, W, [7 ~; A& E    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
) l) C& w& J& H    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
1 @+ h, r& w: q    function balanceOf(address _owner) external view returns (uint256);
. n5 D" l9 t( e) _! I  ~    function ownerOf(uint256 _tokenId) external view returns (address);6 S( `4 A8 q% z9 U% w% n9 z9 N
   
) |( ^3 ?1 _9 h    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;5 f" U& i2 C( y; O1 K8 V
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;3 Q# C0 n8 k2 ]. ~- e
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;7 _5 \$ |3 d/ Z8 u$ a
   
% l1 c& u& ]" w: \7 O    function approve(address _approved, uint256 _tokenId) external payable;
0 h! \/ X$ P$ E( C) {3 ?, }3 p    function setApprovalForAll(address _operator, bool _approved) external;
6 u5 j. [% W4 _& o( m" k. e    function getApproved(uint256 _tokenId) external view returns (address);' B/ {3 b/ ~2 }, l% K7 @+ U
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
& E, m% a, E8 E5 Q}7 |- ^7 }9 l) g* b+ }* o3 R. m
接口说明:
: A; S+ a% a/ N$ _2 `2 D8 W: \- G9 R7 o7 y6 V; ?: c8 h* R
balanceOf(): 返回由_owner 持有的NFTs的数量。; e; d# s5 N9 o/ W/ a

- K6 k- V  _( N$ r+ N6 EownerOf(): 返回tokenId代币持有者的地址。
! n$ t4 U( T5 q5 x7 A4 W, f. t' i/ G8 p% ~9 i6 P
approve(): 授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。
$ _- t6 F+ ~! w7 X; X) R8 ]# v! Z8 W8 u
setApprovalForAll(): 授予地址_operator具有所有NFTs的控制权,成功后需触发ApprovalForAll事件。
4 S- l) B8 b7 ~$ p
# T4 }8 y- E6 i. D1 fgetApproved()、isApprovedForAll(): 用来查询授权。
1 o8 q- S7 }6 L3 S# U( n: o& Z' g

, \+ D' N4 B8 g- B* M0 fsafeTransferFrom(): 转移NFT所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:. C0 X8 x9 g* m% S
( z; X, M$ ~/ @* d. M& L
1 f/ X; I4 `& x6 i& e( A8 h
. v( X- T: ]3 R! ?# O' V: E
调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在监测的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。一个可接收NFT的合约必须实现ERC721TokenReceiver接口:
3 h" n: P+ W- A6 @% R: \
$ ]7 _- {. C7 [9 c& B/ ninterface ERC721TokenReceiver {6 {: e8 R& {, u2 Q
    /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
) R+ x* ~7 n7 ]    function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);
7 a; C1 x: s% L8 s}
! F4 X" N. ?$ g# q$ ~transferFrom(): 用来转移NFTs, 方法成功后需触发Transfer事件。调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。
& ~' n7 |4 k; Q1 a0 C0 H# f6 Y0 K4 `

* q% W6 W, g! iERC165 标准% }3 u# t6 p1 f3 V
ERC721标准同时要求必须符合ERC165标准 ,其接口如下:
; w; s& c% b# D% Z5 Cinterface ERC165 {% J( X7 V8 F# E) W: t4 F9 N) [; m
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
8 L  J' V! p# i% a  E}
/ r7 d8 ]5 d6 UERC165同样是一个合约标准,这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。' M& x" L1 \, u: u% R
interfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));或ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。6 L' [$ O' u$ R  Z
关于ERC165,这里不深入介绍,有兴趣的同学可以阅读官方提案。
% n7 t' T/ _9 e. v4 w可选实现接口:ERC721Metadata
; a/ j6 w; U+ c' N/ X: }ERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。
7 i$ t3 {5 c# `$ l! n其接口定义如下:
* `" E' B" r3 N  d/ }interface ERC721Metadata /* is ERC721 */ {
% m, E# Y" d2 H4 [    function name() external pure returns (string _name);
' P5 b. R8 b5 C: G# s3 R6 g# @, @    function symbol() external pure returns (string _symbol);- @4 \& F* W2 y; L% C
    function tokenURI(uint256 _tokenId) external view returns (string);: j/ W' ?5 |0 }) a% a. h: J# f
}
& e% R/ E7 [3 T7 L0 y接口说明:" {  J2 O- h" l; D3 z. u
name(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:
( h: T; q# O1 \2 O7 ?

* J' \4 k8 c" U3 B{; n* a. J6 ^; f  R
    "title": "Asset Metadata",, M0 m, O; Y4 v, u1 k0 ^3 I
    "type": "object",
9 o$ U+ \0 K  N3 L* b, e    "properties": {3 T  R& z8 D& _2 ^5 M
        "name": {7 v2 {4 w$ d. V( [
            "type": "string",7 z% O2 u( Y% [9 R9 L+ w' G
            "description": "Identifies the asset to which this NFT represents",+ h7 _  N% k& {
        },
9 s/ e9 _" U; \  q, S/ f. V1 B8 P        "description": {
9 L" h3 J' I( d+ t& `            "type": "string",
$ h2 e# [6 v! R            "description": "Describes the asset to which this NFT represents",
( V# @- h  K0 i: Y! j0 e: P        },# a5 W9 u1 g, ^
        "image": {# x3 D! e) z% v/ L  L9 M7 p" L
            "type": "string",
5 `7 t* e! u# ^9 R# p% Q% S            "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- i2 `4 R% S        }0 l& W; N; f1 e# Y2 q5 z- V
    }: L$ ]7 A1 ~7 Q. ?+ X
}
' H- b) x% ?# y/ I- L" F3 PtokenURI通常是被web3调用,以便在应用层做相应的查询和展示。% r, l/ L/ C% ^% v
可选实现接口:ERC721Enumerable9 j4 s* g8 `4 V5 b: |* L# ?
ERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:. z2 N7 {* ]  a9 _
interface ERC721Enumerable /* is ERC721 */ {
5 I/ e& l' G* Z. ^& S0 s' w    function totalSupply() external view returns (uint256);
( C' D1 x& @1 D6 D/ U: ]% {    function tokenByIndex(uint256 _index) external view returns (uint256);
( t$ O& M/ T: Y5 O& l3 N' U8 s  t    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);( o8 w! i6 `! Y# B
}
/ y' o: v3 C$ S! Z1 {, G- }5 u! \接口说明:
; H0 Z! F* k0 x9 j5 w$ F- RtotalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。
0 {& V" B* W4 x3 ~  I

- [, _! Y5 |( }  e补充说明3 ^& l% j; x( c* G3 K/ A% Q
NTF IDs" n: `2 C- L; \8 X
NTF ID,即tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:
* u6 F! \: J& k" d5 r& A
" Y/ r$ w$ z& \' h$ L/ `# J9 z从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID
3 A$ n: S. |) t' y' }3 c
  T+ E% F% y. W与ERC-20的兼容性6 i( x7 ]+ |3 E3 g, M
ERC721标准尽可能遵循 ERC-20 的语义,但由于同质代币与非同质代币之间的根本差异,并不能完全兼容ERC-20。
& m% h2 O1 b1 D0 ?2 @' s: D交易、挖矿、销毁& c/ f0 {( N. I+ Q
在实现transter相关接口时除了满足上面的的条件外,我们可以根据需要添加自己的逻辑,如加入黑名单等。
& c" h9 h( H  C. ?+ p同时挖矿、销毁尽管不是标准的一部分,我们可以根据需要实现。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    10