OpenZeppelin ERC721源码分析
杨远枫冠
发表于 2022-11-25 20:49:28
2401
0
0
+ @. M. `0 `' t: J; p; v
它和ERC20有所不同,ERC721最小的单位为1无法再分割,代表独一无二的,针对不可置换的Token的智能合约标准接口。从ERC721标准草案中可以看到,兼容ERC20的方法有4个:name,symbol,totalSupply,balanceOf添加的新方法:ownerOf,takeOwnershipERC721还重写了approve和transfer。
ERC721Basic.sol) @/ J% ~* @" G; Z5 m; L
pragmasolidity^0.4.23;3 Z- G& Y* N) l: H) |9 J
# b' _+ X# n9 m9 V2 R+ k! i
/**
*@titleERC721标准的基本接口0 x5 r' v$ W: y0 \
1 w# h) J2 \: M
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
' ^2 K) G8 }5 }: o8 M# \. p
*/; k% b& B& ]! `8 g! l, C
contractERC721Basic{
* |' \. S' q, [ f) K
eventTransfer(% F$ r/ f# b* F
: a% k4 X2 Z$ f" ^) D6 t: F
addressindexed_from,
& }! t1 Z' X- S. a' A5 B2 e9 J
addressindexed_to,+ y- T( F$ C( K8 N
& y+ G j8 p' {$ `
uint256_tokenId0 ^! y6 |7 a6 M9 h, J& i. W+ B5 y
);5 i6 |+ O1 F- E$ x8 Z4 Y% [9 O8 S
4 q2 W5 f' D+ j# M2 V9 p
eventApproval(
6 E) h0 ^: U+ k! K' U
addressindexed_owner,
addressindexed_approved,+ u8 U2 t8 d4 I$ ` `& y" o+ w
uint256_tokenId' I G4 k( Z# E
);
eventApprovalForAll(2 c# ?( b% {8 t W
addressindexed_owner,
' n% c. e/ Q* H
addressindexed_operator,3 \% G& R) d( I, g
1 K/ c% T# L; b
bool_approved4 e g1 L8 q3 Y1 F ^# [4 M
);
L0 |' f$ T) {* Y
functionbalanceOf(address_owner)publicviewreturns(uint256_balance);
/ A1 B& ~/ w5 \) h5 c, d- I
functionownerOf(uint256_tokenId)publicviewreturns(address_owner);! O% E9 u! k. S
functionexists(uint256_tokenId)publicviewreturns(bool_exists);- m( A. @8 x/ G. R. b% s
functionapprove(address_to,uint256_tokenId)public;
functiongetApproved(uint256_tokenId)" W, X/ b: W$ L @& Z- T5 t
publicviewreturns(address_operator);
functionsetApprovalForAll(address_operator,bool_approved)public;7 I3 o: W+ C2 D; u% W3 }! y
functionisApprovedForAll(address_owner,address_operator)
publicviewreturns(bool);" B4 B4 u; O$ f' }5 H; y
; o+ K5 E3 T; m4 n: i
functiontransferFrom(address_from,address_to,uint256_tokenId)public;
functionsafeTransferFrom(address_from,address_to,uint256_tokenId)
public;/ f& {6 E5 i* R! ^
- q) {% `: O( l* `# u+ {" T; X
functionsafeTransferFrom(
address_from,9 n' `! Q4 B- A( \' E- g/ f+ z
# L4 I' O) c/ G Z( v' M
address_to,
& C/ Y( a7 z: s
uint256_tokenId,3 `) @2 k T6 W4 z' I2 p
bytes_data4 I7 f: C- T- w6 Z! s- z
)" G) N/ S, i* J4 e8 `8 j) J
public;
) v# |' ~3 h4 X& C& v/ m4 g
}# {2 X& |9 g$ `5 [9 g" N& S
ERC721Basic合约定义了基本的接口方法:* [7 D' W( b' K0 _
& o) {; Y4 c% J# a3 N& Q& X- c7 d
balanceOf返回_owner的代币数量
ownerOf根据_tokenId返回代币持有者address
6 n" Y6 U1 W+ C R% l! l
exists_tokenId是否存在
- p/ I; g! Z9 v" e/ w
approve授权_tokenId给地址to
getApproved查询_tokenId的授权人_operatoraddress% [$ E4 i) P5 u
, u0 q7 D: m3 k
setApprovalForAll授权_operator具有所有代币的控制权2 O1 a( P4 {! U& o: Z
6 |( S: a) p/ R) {8 r( g
isApprovedForAll# U9 {! W; R a C
transferFrom转移代币所有权
safeTransferFrom转移代币所有权- V/ a! `* k* V/ u
同时还定义了TransferApprovalApprovalForAll在后面的ERC721实现的代码中再来看事件的触发。/ i6 p" U) I0 `; P. }8 h$ ]: m( {- A
ERC721.sol5 Z: G: d4 I1 C0 p" i0 F+ s
. B( r) }* m+ o
pragmasolidity^0.4.23;
) K+ F/ L- I1 o# Q
import"./ERC721Basic.sol";; D& d; y. j+ Z4 Z& q# R
/**( h3 y( U! ` J/ z/ `" q
; a/ w. S3 \# B+ w! } J' J
*@titleERC-721标准的基本接口,可选的枚举扩展5 w2 ~- V+ l* @2 t
7 Z( y. ]- _3 Z4 Z6 j5 R2 i- u, e& c
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md ]7 V# g) W N. Y
*/: {$ m6 R4 h1 a2 \- h6 v+ W" q
, O$ R& Y4 }! z( }8 a
contractERC721EnumerableisERC721Basic{
: k n( G7 @8 a+ Q; u
functiontotalSupply()publicviewreturns(uint256);
functiontokenOfOwnerByIndex(2 j9 n' S6 A9 }2 V; S/ h% g1 u
address_owner,
uint256_index
)8 }1 g, J' M" M) W, q8 I
& W9 s( a7 Y7 M9 Z1 W& _" u( v
public
' |! @ ?) a( c! x' L
view
$ n( p- c6 N* J7 c
returns(uint256_tokenId);" v+ S. u i: _7 J
functiontokenByIndex(uint256_index)publicviewreturns(uint256);/ @4 b( ^8 n. v- U
}
, |8 Q; B* S/ L% t
/**
*@titleERC-721ERC-721标准的基本接口,可选的元数据扩展
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md. N- e* K1 B8 o% e
8 g* o# X- k; M! V6 H
*/- f( K& J$ e7 M2 d, ?
7 l1 [2 d; k/ [; ]) `
contractERC721MetadataisERC721Basic{: l& s! @; p, J# R. H" D- J
functionname()publicviewreturns(string_name);
functionsymbol()publicviewreturns(string_symbol);; k# x1 ~2 A, _- I* [3 a
. D6 W9 `8 W9 `# x* o- d
functiontokenURI(uint256_tokenId)publicviewreturns(string);
}7 m4 y; B) k) x- S
/**# p4 `; |! V2 x9 n
*@titleERC-721标准的基本接口,完整实现接口
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md8 |9 A+ ?! Z$ G3 M0 P+ ]
, o, w* i9 k6 @
*/
1 i& j1 H6 f# Q. u s
contractERC721isERC721Basic,ERC721Enumerable,ERC721Metadata{% t5 L. ]/ g3 W
; O' u' {1 c: Q# C& n# g
}. v! S5 A! _* P5 H4 Q5 f
7 I$ {: B6 I. J F( T) c. E) R# k" d1 b
ERC721合约继承了ERC721Basic的基础上,添加枚举和元数据的扩展。( E9 c- W0 p H8 B
ERC721Enumerable枚举扩展可以使代币更具有可访问性:
totalSupply返回代币总量& E2 {( b; i9 @, ` X# E* g( [
tokenOfOwnerByIndex通过_owner所有者地址和索引值返回所有者代币列表中的_tokenId
5 p6 O2 k4 _; p; `
tokenByIndex通过索引值返回tokenId
; |8 w% ~& f+ I+ C) E
ERC721Metadata元数据扩展哦用来描述合约元信息* n3 F! ?4 r9 M! r3 S, [
! w+ D# `' p1 D+ z* |" l$ C
name返回合约名字* C e9 c5 y3 J. A, r4 m: r
: F2 a5 u5 U3 I! s
symbol返回代币符号' v, i# K4 Y+ U$ s( @' q9 c
) L6 B3 J1 G7 I3 O; _/ Q) G- I
tokenURI返回_tokenId对应的资源URI& v# z* ^+ ]9 g! Z5 F
, R l9 C! h4 j" L/ |* p$ u
ERC721BasicToken
ERC721BasicToken4 X* w% t x& m& y) y, } c; t
pragmasolidity^0.4.23;1 a' H% s+ g6 r e: K% |0 e* {
1 ?0 E' K. _' }0 y* n$ f
import"./ERC721Basic.sol";4 G8 K7 H6 q6 y1 {. J$ Z
6 Y0 j% C& I& c9 y4 u) T9 u
import"./ERC721Receiver.sol";
- x7 V8 T* D& D6 Z' `8 Z
import"../../math/SafeMath.sol";' @8 x/ ^9 F- v: O6 N, ?1 l6 o
" f2 ]' [& C- Y. x, a% X
import"../../AddressUtils.sol";& ^$ W% l% z7 c% s$ |8 e K9 b5 [
; S- ]: K5 L/ \; N( r; l( q8 j
/**. w6 a) Z) M. g5 a9 i% M9 \
) C$ Q4 C6 y4 I
*@titleERC721标准基本实现
9 e- V. |* k" W1 S" T2 B% d3 V
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md6 T0 p) Q; p, v% L, }. |
*/
contractERC721BasicTokenisERC721Basic{
usingSafeMathforuint256;) Z9 M% _, i, {. Z" `5 r5 N( o
$ g0 f9 X1 W& U* u
usingAddressUtilsforaddress;9 `/ l0 X; Y. C. Z; I$ W4 T
//Equalsto`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
2 J/ n; B' W- r/ i- y
//whichcanbealsoobtainedas`ERC721Receiver(0).onERC721Received.selector`
bytes4constantERC721_RECEIVED=0xf0b9e5ba;8 G; |( x4 ]7 w5 }+ q/ f9 C
: L) \! s3 g+ U4 h; ^
//tokenID到持有人owner的映射! U% t7 m2 G7 C" m' @/ |
mapping(uint256=>address)internaltokenOwner;+ W5 l3 m5 p% L5 e4 P5 y5 c1 C" R
( r' o0 X3 v' o
//tokenID到授权地址address的映射
mapping(uint256=>address)internaltokenApprovals;
! S9 e: ]$ a( y+ Q
//持有人到持有的token数量的映射
" o: ] u) ~* H
mapping(address=>uint256)internalownedTokensCount;" m' X5 }# K" z0 ~
( t0 d# v9 Q+ u( F4 M+ d, z& L
//持有人到操作人授权的映射
mapping(address=>mapping(address=>bool))internaloperatorApprovals;
" K) l. }5 Z5 Q% z( s
/**
*@dev确保msg.sender是tokenId的持有人" R+ ^, ]' c2 W. O9 ~
; K1 N2 `& x) \1 e4 t1 ?# p
*@param_tokenIduint256IDofthetokentovalidateitsownershipbelongstomsg.sender8 f( g4 P! b+ N' c
" }, W1 G* Y; i+ S
*/
. l6 ^; M& F5 N# }1 D% W! u
modifieronlyOwnerOf(uint256_tokenId){
require(ownerOf(_tokenId)==msg.sender);
. s- D, y% x: X0 Z9 K
_;
/ {7 L! H: K' ?, z* Y1 r% M. i7 j
}$ G- S, c" i9 m1 u% k
/**5 e7 l. l+ V; |! j8 {
2 R+ A+ d4 p1 E. |
*@dev通过检查msg.sender是否是代币的持有人,被授权或者操作人来确保msg.sender可以交易一个token. N" L7 v) l9 ?: ]* S C& V
8 G Z. H5 i* d- f7 P" a/ ^
*@param_tokenIduint256IDofthetokentovalidate
. f A7 J. m! f- O m; k
*/ O4 N4 m+ e p+ ?5 O
modifiercanTransfer(uint256_tokenId){; I2 ~! ?8 Y7 d7 H1 ~
require(isApprovedOrOwner(msg.sender,_tokenId));
_;: ^* l/ ?: U7 ?1 @( o. s/ h2 P
}
/**. u! u `3 M0 P/ U& W
, D7 d0 W& W1 [( l, P
*@dev获取持有者的代币总数
& T7 m$ z' g1 s( ~& r
*@param_owneraddresstoquerythebalanceof: ^, |2 M6 B2 X& N4 c% A
*@returnuint256representingtheamountownedbythepassedaddress
*/ ~. z/ s$ |( \- Y( @0 {
functionbalanceOf(address_owner)publicviewreturns(uint256){! w! m3 Z, ^5 t/ Z( e
' v1 m5 v8 f' [. O# ?. B+ K
require(_owner!=address(0));
returnownedTokensCount[_owner];3 m& I9 t9 p2 T9 y4 I
5 x1 D& X6 u& e
}
) k$ ?2 X, [2 i* ^0 O9 C. h. k# T) z
/**. O a2 A8 p5 J2 @; M4 i
*@dev根据tokenID获取持有者
, m# C( [. s3 ~2 {
*@param_tokenIduint256IDofthetokentoquerytheownerof) S( J6 w* h/ G- \3 o" R% w
*@returnowneraddresscurrentlymarkedastheownerofthegiventokenID
*/
functionownerOf(uint256_tokenId)publicviewreturns(address){& C$ H( ]8 x3 s5 I7 O9 ], q7 r
addressowner=tokenOwner[_tokenId];
require(owner!=address(0));: E& b) L7 J- m) k# N
returnowner;! S6 ^$ r# F$ X: ?7 |
- Z, n1 t) A5 \& e) ^
}
/**
7 {( m6 W# f1 ~; |% Q2 N2 n
*@dev指定的token是否存在
*@param_tokenIduint256IDofthetokentoquerytheexistenceof0 T2 I D* P5 y3 y
& f5 [* V4 W) d8 k
*@returnwhetherthetokenexists
*/
: d- o- D) ?+ q: u9 p. q" f; ?( Z
functionexists(uint256_tokenId)publicviewreturns(bool){
addressowner=tokenOwner[_tokenId];0 H) w+ y7 K0 z1 R. [5 C, ^0 V3 _
6 j' S+ K1 g# I5 E
returnowner!=address(0);
|7 t: K( [* A' o# R7 J! ~
}4 K$ z0 k; y) i0 N. X
/**- K$ C) G$ ~3 e# @( e% Q
*@dev批准另一个人address来交易指定的代币, @: {1 {& s* i- ?- z
*@dev0address表示没有授权的地址
*@dev给定的时间内,一个token只能有一个批准的地址4 Q/ b. T. \, q' [# V: ]3 g
, }4 ]! J- k. Z4 h8 B& ~9 q0 l
*@dev只有token的持有者或者授权的操作人才可以调用5 T2 P) S- I0 R
& }+ k# w( C& M
*@param_toaddresstobeapprovedforthegiventokenID
8 l+ ] K7 Y4 ^3 F) d8 \( W) a. _
*@param_tokenIduint256IDofthetokentobeapproved
8 w7 Y9 |! H- a _
*/; k" d$ e: L3 i: p
; b9 ]0 l# l7 N. y% [# r; j2 l
functionapprove(address_to,uint256_tokenId)public{! x: c+ C2 O2 `( D) V2 }3 k
. c, _! r7 k" F% x. L. @0 I. s# O
addressowner=ownerOf(_tokenId);
require(_to!=owner);. r7 H5 c' p/ \: f
require(msg.sender==owner||isApprovedForAll(owner,msg.sender));
* {5 h1 |2 N& \6 ~& e4 p) j
if(getApproved(_tokenId)!=address(0)||_to!=address(0)){; i; y# f q$ P5 I
4 U4 F6 @! S( x; B! M0 G
tokenApprovals[_tokenId]=_to;0 k+ t4 I& m% n4 d! }' O
, H( ^8 I3 ?( M' K4 p
emitApproval(owner,_to,_tokenId);3 ~& b' X* f( l ~$ a9 Z& x
, n: e( f" J: V+ K
}4 |7 R4 x# L) b4 y8 o
}
/**4 r# X1 X8 E* c4 S
u% v6 F) y8 k9 Z8 j% X P" H" ?
*@dev获取token被授权的地址,如果没有设置地址则为0
) L; p2 u# S) w( Y7 }
*@param_tokenIduint256IDofthetokentoquerytheapprovalof2 I! c) H) k7 f, T3 A) y
- G0 n0 ^. K0 w
*@returnaddresscurrentlyapprovedforthegiventokenID2 c7 L/ G; V* _9 _: c$ i0 O
*/: D* } [; m2 B& F: U, V& O
functiongetApproved(uint256_tokenId)publicviewreturns(address){
returntokenApprovals[_tokenId];% A* P6 i' T" x6 T% J* x. Z; w
- _3 A/ m, J* F1 C
}
/**
! K& d8 X3 v9 ]/ Z* j- U
*@dev设置或者取消对操作人的授权
4 i$ a6 l& Z% V; E
*@dev一个操作人可以代表他们转让发送者的所有token
8 X$ U$ S3 d) `( ]0 m2 x8 K3 T
*@param_tooperatoraddresstosettheapproval0 k( n& W+ v$ C' Z" {
) a+ Y, e p _0 V/ k
*@param_approvedrepresentingthestatusoftheapprovaltobeset
*/
6 v! ?4 T( k6 Y# h) M
functionsetApprovalForAll(address_to,bool_approved)public{6 R8 m, h7 [( n
% C) W7 F \: j
require(_to!=msg.sender);* g! L) @! C) [ ^
operatorApprovals[msg.sender][_to]=_approved;; Z0 z a/ K% ]( k. T9 c& r, D* L8 b
emitApprovalForAll(msg.sender,_to,_approved);# p& B. D( I5 @4 W
' _6 P9 [& \. a
}
/**
*@dev查询是否操作人被指定的持有者授权# V0 C- t6 `3 ]5 `" `/ I
*@param_owner要查询的授权人地址9 u u ]/ j! I; C
*@param_operator要查询的授权操作人地址& a( j( R( Q# f3 |5 g9 O4 u
*@returnboolwhetherthegivenoperatorisapprovedbythegivenowner2 `: Z" {/ y9 q2 t8 l4 E9 q; A
*/# G$ k# H. _+ W3 i" ~$ w. y
functionisApprovedForAll(
; C. Z' z8 m, L; v) Y) G
address_owner,
' X- _. F8 \3 k3 c
address_operator
)
public
view+ m# C8 r* X2 Y W7 o2 N* R
2 j$ ]( N) A0 R. r. k) `" z
returns(bool)$ i4 u. u+ l) r3 d& E5 x
- v! j8 S$ x/ H. ?* I* }
{
# O+ [: x/ u* G; c
returnoperatorApprovals[_owner][_operator];7 W: O: U; o" N2 }8 m
5 J7 `) w8 k9 i6 v$ e$ |
}
/**
" v0 C$ e6 z6 }; S0 w5 S7 [* _4 J
*@dev将指定的token所有权转移给另外一个地址( x2 K$ Q! g( y
*@dev不鼓励使用这个方法,尽量使用`safeTransferFrom`
*@dev要求msg.sender必须为所有者,已授权或者操作人. Y/ f& H) s( @: o/ F3 u
*@param_fromcurrentownerofthetoken+ m; A4 j/ x6 U7 i5 g
*@param_toaddresstoreceivetheownershipofthegiventokenID
*@param_tokenIduint256IDofthetokentobetransferred7 N' I" d% i" e+ H" Q" P
2 W& A$ J5 M( v0 C
*/
. b1 g( X! q5 C6 C# v' S
functiontransferFrom(
address_from,5 d( b; {# `+ O+ j) g; T( [
! J$ L+ f- S# c( X
address_to,/ P. o7 U- E; {
uint256_tokenId
)
public9 j H; L4 D( i
6 V6 B6 b% @% M) K# g
canTransfer(_tokenId)8 c3 B9 \5 @1 _6 c5 D& Y
. ?$ l6 w* _5 e! D/ I3 ]
{) y! A9 ?2 c# j& e0 u; V
: l! k" ^( Z% I1 J7 P& e" m8 v
require(_from!=address(0));5 p7 h3 X: s5 B8 Q4 r4 e
" ^ ~* ~. f( ]) a' @, d8 }2 G
require(_to!=address(0));, }8 |5 g, U' `- X6 m8 Q1 C; K
clearApproval(_from,_tokenId);
removeTokenFrom(_from,_tokenId);
addTokenTo(_to,_tokenId);; |* r1 E( ^* i1 E2 A* k" z6 l
" [1 W$ w& ~% K2 E. |8 e
emitTransfer(_from,_to,_tokenId);
6 o- x3 Y, `, v7 D5 q1 s3 A
}
/**( T# x |& [7 e
8 V v; |% a/ {4 k; m
*@dev更安全的方法,将指定的token所有权转移给另外一个地址, Q, A- q) N! w( Z
K$ U: e0 a4 J. b- a, y! k
*@dev如果目标地址是一个合约,必须实现`onERC721Received`,这个要求安全交易并返回值
! F) M9 X' Y. k4 y4 w# p- S' V `
`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`;否则交易被还原
*@dev要求msg.sender必须为所有者,已授权或者操作人5 M7 s6 d0 J6 t+ ^. r& p
*@param_fromcurrentownerofthetoken
*@param_toaddresstoreceivetheownershipofthegiventokenID
*@param_tokenIduint256IDofthetokentobetransferred/ }. p3 b0 Y& `9 t, c
*/
functionsafeTransferFrom(# c+ ^( I; l, X: h( J* U
( G0 V. H4 k& q1 ^: G
address_from,
# |7 j* i" m4 x
address_to,
uint256_tokenId; V3 f% r0 e+ U' r2 r* \0 {" |
)
public8 E1 Z. V: L N9 }( V% S3 _3 o3 T
( j2 S) u; L# N2 }* h4 c
canTransfer(_tokenId)+ O: [% W q$ }2 T/ c4 x
& X% D7 R2 O/ e8 C
{: ?! I& _2 e" V7 l" W# U& X
; I$ z& D4 P% w2 @1 i
safeTransferFrom(_from,_to,_tokenId,"");
0 L7 Z) |$ X( R( N
}& B$ Z; ~$ W5 H3 V0 {
& V% l/ Z' R# K* ^
/**$ C. J: X3 \& ]/ {, G% U/ A
*@dev更安全的方法,将指定的token所有权转移给另外一个地址" T8 s+ X* i5 G" C3 Z# e
2 j7 b. h6 \9 F9 \0 q
*@dev如果目标地址是一个合约,必须实现`onERC721Received`,这个要求安全交易并返回值
`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`;否则交易被还原
*@dev要求msg.sender必须为所有者,已授权或者操作人8 y; i/ S6 H. E( R0 _, V3 c
*@param_fromcurrentownerofthetoken
*@param_toaddresstoreceivetheownershipofthegiventokenID0 s z Z/ |+ k3 l; h, g5 o
5 Z* l/ w0 g# P1 g1 E& L
*@param_tokenIduint256IDofthetokentobetransferred
*@param_databytesdatatosendalongwithasafetransfercheck
3 y0 @; \' G- p. L" ~8 M7 W5 Y+ o: r
*/1 {- E) z+ O5 }- B4 i
$ B0 B! j' J( v- K( c! I V D
functionsafeTransferFrom(3 o9 q: x- \% @
4 p/ x" k+ E5 L# \$ v: ]( s
address_from,
address_to,$ ]0 y0 @# L! W5 T( O9 m a
uint256_tokenId,
bytes_data7 W" h$ J" a# t" g
)% B% l5 R4 v# e ^ w
$ w$ d* O E" w8 E# H9 l) ^
public5 X: } _! s! w2 [8 t% Z
canTransfer(_tokenId)3 q) L' p$ w. Q! a
; d8 S! \' ?; S, i2 w2 M
{
transferFrom(_from,_to,_tokenId);
require(checkAndCallSafeTransfer(_from,_to,_tokenId,_data));- F5 G. r8 @& |, i
7 A& e+ l( o: K# f0 S& c
}' ~( e- G( v7 A/ [, c
/**3 H% P1 b( q* P+ q. Z3 h2 V
/ ^9 f# a. R8 l' K; ?8 C
*@dev返回给定的spender是否可以交易一个给定的token
: h/ g1 G- G# G0 ~, `( ^% e
*@param_spenderaddressofthespendertoquery
*@param_tokenIduint256IDofthetokentobetransferred
*@returnboolwhetherthemsg.senderisapprovedforthegiventokenID,& m, a3 r# I% V
*isanoperatoroftheowner,oristheownerofthetoken' j( X7 m- H" F: `
*/
. k1 A' O1 L5 r# ]9 `6 V' w
functionisApprovedOrOwner(
, e5 @8 ?& S9 Y+ a. M
address_spender,
9 X" d: ^# Y0 _# |9 C# g& N9 P
uint256_tokenId
)) l( y1 r- d: t, _/ R6 r# n
internal2 Y4 S w! y* a9 }. b0 X( p) ~2 R8 [2 q& m
view9 Q+ T( V( m/ ?/ K
returns(bool)
8 A3 Y0 e6 Q" I
{
addressowner=ownerOf(_tokenId);
return(( F* a9 `5 I, {% I, m
_spender==owner||
K9 Z/ F. e- n2 [' u- B
getApproved(_tokenId)==_spender||/ Z9 ^# s4 {! E: U2 m% K8 A
isApprovedForAll(owner,_spender) m8 c' x! t+ R- c0 H
3 Z* S$ |! L, r; ^$ K+ F9 \
);% X3 {, X: Y# ]5 N6 m
}: L {% r2 K8 N6 X8 U( N
/**& w6 s! N. o% x; q6 f7 P$ v, s/ m
*@dev增发一个新token的内部方法- `. A0 x% ^+ K, ]* x2 E% @
/ _) [* A" V( @
*@dev如果增发的token已经存在则撤销. H, x" j( y- O% z1 F9 }% v
*@param_toTheaddressthatwillownthemintedtoken" Q! a: ?$ _- v) W5 w
1 A0 s0 r; l' s8 A
*@param_tokenIduint256IDofthetokentobemintedbythemsg.sender
! M0 t* y1 a3 K0 ^; K$ I
*/3 @8 d7 o8 k. l9 [
& _: }. A! E4 N
function_mint(address_to,uint256_tokenId)internal{
, f$ U9 q; T( h5 l: a: e1 W
require(_to!=address(0));( F. |9 q# z) C5 P5 N: ?* y
" i7 Q. i- j& U! V, s
addTokenTo(_to,_tokenId);; }# K8 `1 b+ U, v- A( s
emitTransfer(address(0),_to,_tokenId);
. w9 ^% A5 Z+ w, c5 m1 F
}
6 l' S. _# w3 T& d' A
/**
' k9 u# Z( E" P' B0 `7 K. _
*@dev销毁一个token的内部方法+ B* b6 ?& t& F
*@dev如果token不存在则撤销
*@param_tokenIduint256IDofthetokenbeingburnedbythemsg.sender
; F- ^5 a3 E8 g, {. C
*/7 [9 Z& ]& c5 @. x* ~' t1 q4 g! g
function_burn(address_owner,uint256_tokenId)internal{7 K8 o7 J( [. K4 }( J( k
clearApproval(_owner,_tokenId);9 u7 z* d3 y) j8 H6 m' |" m
, m- [/ |2 J8 H( P o! Q$ y
removeTokenFrom(_owner,_tokenId);' e! |# g! t0 K9 T
emitTransfer(_owner,address(0),_tokenId);8 ~5 [' a+ D; G' y4 B
- Q& ]: u; `/ ~' r+ y
}
2 n0 J- N7 M. Y+ _# H4 p5 E& V7 T, c
/**& U5 j) C, Z2 y4 N
*@dev清除当前的给定token的授权,内部方法
*@dev如果给定地址不是token的持有者则撤销
*@param_ownerownerofthetoken
5 O4 P/ j4 X0 }
*@param_tokenIduint256IDofthetokentobetransferred5 }) ^' J0 T7 U/ f' M
7 r1 p4 J( i+ Z6 I* Y
*/
functionclearApproval(address_owner,uint256_tokenId)internal{" U& O' O- u1 J2 h9 x
3 o5 o4 u4 ?% q3 o: K, G* u
require(ownerOf(_tokenId)==_owner);% d o& |/ a- o% t
$ y* J/ V- ?" P4 b/ z
if(tokenApprovals[_tokenId]!=address(0)){
tokenApprovals[_tokenId]=address(0);
' X9 u4 B* y. O) f/ \- k
emitApproval(_owner,address(0),_tokenId);9 D% S y' f" `9 H9 f0 O
% ^% J( z4 N# C! l9 H0 \
}' V% d1 |+ z' M/ c7 k% i
} |3 h8 Q9 R V, J, ]
/**
- Z8 @- `7 t* ^( T
*@dev内部方法,将给定的token添加到给定地址列表中
/ o0 Q& P$ z7 g! r
*@param_toaddress指定token的新所有者- R/ Q2 a+ }2 {
- W% e. _7 o+ u' a! Z2 n
*@param_tokenIduint256IDofthetokentobeaddedtothetokenslistofthegivenaddress; Y: ?, K. x+ `; V) L2 M4 w
" j( P0 ~6 d( w R$ v. O3 |
*/
9 h" d) W3 r- y
functionaddTokenTo(address_to,uint256_tokenId)internal{
require(tokenOwner[_tokenId]==address(0));! v" J I* G2 B$ l" R! Y
1 {+ ^; P% o& T4 M2 ?5 b
tokenOwner[_tokenId]=_to; X* R# U0 `; F/ r- @/ {5 ^. j
, |- c2 l# ]: `
ownedTokensCount[_to]=ownedTokensCount[_to].add(1);2 R7 ?; m8 I: Y+ Z" a! W
* ]* C3 `. x4 H, ^
}
/**
9 k' m" _7 `( T1 D$ _
*@dev内部方法,将给定的token从地址列表中移除
) ]8 p5 c8 J' g: K# K
*@param_fromaddress给定token的之前持有中地址. C# a; H; m2 V5 |
+ y2 k+ G4 p; W, n; U
*@param_tokenIduint256IDofthetokentoberemovedfromthetokenslistofthegivenaddress! n+ u1 m; v2 l3 V, R9 g1 t
*/+ V7 c8 f: C0 @8 h
/ i" R6 _+ P+ j$ L
functionremoveTokenFrom(address_from,uint256_tokenId)internal{% M/ e& ~6 o* T7 |
require(ownerOf(_tokenId)==_from);
ownedTokensCount[_from]=ownedTokensCount[_from].sub(1);
$ |" m3 i: M, c. V/ _& L: L
tokenOwner[_tokenId]=address(0);2 X. ~% R/ o: ?1 ]+ U7 n
}
D$ V- T8 y+ _" E) B W2 U. `
/**
*@dev内部函数,调用目标地址上的`onERC721Received`
) G( A: C% d. V2 |: y7 ?
*@dev如果目标地址不是合同则不执行调用
+ C7 w2 Y7 ~ S3 h
*@param_fromaddressrepresentingthepreviousownerofthegiventokenID
*@param_totargetaddressthatwillreceivethetokens
9 v0 U& e3 {- T
*@param_tokenIduint256IDofthetokentobetransferred
*@param_databytesoptionaldatatosendalongwiththecall8 `$ F: s2 U& n. k3 p& ^1 p3 A) T
*@returnwhetherthecallcorrectlyreturnedtheexpectedmagicvalue/ F$ K# d6 q# u3 m" _- \; k. q
*/( ^. q: |7 ~" J" f3 H
6 U' i8 X, b2 _3 S1 J" n i
functioncheckAndCallSafeTransfer(
address_from,. J+ S- J( W/ B' `& U
address_to,, a# }% J( Y( x: {2 F4 H5 l
uint256_tokenId, p: T1 ^5 t9 m' O$ |/ g
bytes_data$ @& ^8 B9 P6 W
4 Q) N' ~; ?. K7 B5 Y7 H0 q
)- ^5 p# G3 L) ?) R% J, O
e4 h9 i5 I5 K" l" m/ H
internal
returns(bool) Y4 D; q5 C f! V
4 i3 ]; @1 t* M* ?: O
{
* h/ L$ X: n0 i* e; n
if(!_to.isContract()){9 e9 ?) e" m$ a: y3 F
returntrue;
* L P6 V' @3 f0 J5 Y- e7 b
}
bytes4retval=ERC721Receiver(_to).onERC721Received(( V8 g: w% P8 k" j) I5 n) @, T2 c& g9 h
_from,_tokenId,_data);
0 P. P5 r0 Q8 A0 g2 j8 i7 ^5 ]
return(retval==ERC721_RECEIVED);: N- N9 Z: K. C4 [; j: b) c# C
}
}
ERC721BasicToken实现了ERC721Basic合约定义的接口方法,主要对token的持有人的一个添加和修改,以及授权和交易的管理,实现了基本的非同质化token的业务逻辑。具体方法实现并不难,就是对映射的公有变量的管理,但是对于权限和安全验证值得关注,比如函数修改器还有require。
, M6 ?6 ]+ E* q% w, h1 Q
ERC721Token.sol- k: |3 u' F# F+ J3 w% F% F, t
pragmasolidity^0.4.23;
import"./ERC721.sol";
. _6 K6 C5 F* q8 j" _! i5 {$ t/ |
import"./ERC721BasicToken.sol";
/**" L4 U) V7 [/ H% Z3 I% s0 _
*@title完整ERC721Token
*该实现包括所有ERC721标准必须的和可选的方法,此外还包括使用操作者批准所有功能4 F0 R; Z" `4 R% }: j
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md; d! I/ Q+ Q, |4 d
*/
contractERC721TokenisERC721,ERC721BasicToken{) m& F+ H ~7 N+ Y |, J# W" B
//代币名称
stringinternalname_;6 b" \% K4 u. D" I: I4 d
//代币符号2 s0 b8 W* B& O# g
: x4 n, N/ P9 T+ z2 \; e9 s) l
stringinternalsymbol_;
1 _9 d/ W! \, V& m: R2 g
//所有者到所有者拥有的代币列表的映射
6 ?& k/ J9 c1 K" z9 N$ e% a6 {
mapping(address=>uint256[])internalownedTokens;
# |, }# }, q, }, C2 R
//所有者代币列表中代币ID到索引的映射
) z( a. C" g. G$ E% ]
mapping(uint256=>uint256)internalownedTokensIndex;& Z6 V" ]' T% D. a" d
//保存所有代币ID的数组,用于枚举
uint256[]internalallTokens;
) g9 ]8 z' ?3 h
//allTokens数组中代币ID到索引的映射
3 ]1 R. j# @1 z4 _2 I+ v
mapping(uint256=>uint256)internalallTokensIndex;; O }% K" h2 Y
& |5 y( ~8 m6 s# }) q% e1 p+ E* Q
//可选的代币资源URIs映射
mapping(uint256=>string)internaltokenURIs;
9 H2 J7 k' k1 |# z4 O6 e
/**3 f9 S x a5 g& V( `
! n0 v; t i% f# {
*@devConstructorfunction6 Z& n& j- d! x" c
*/
% r: J D/ w1 l @6 g
constructor(string_name,string_symbol)public{
name_=_name;
8 }0 }- y, l0 C) C( J, s! p! Z3 r
symbol_=_symbol;
}' H/ P \; q( k$ G) q
" c8 T9 t! S) W* p
/**4 ]& J8 h K5 Z6 {( m9 x) h1 _5 I/ w
; N! B7 Q& ?- S$ O
*@dev获取代币名称% n1 z6 ~6 y9 F) p
9 W a* Q# W0 p- P9 G
*@returnstringrepresentingthetokenname& a" r% M7 `2 B6 [1 u' l" Y4 |
1 @4 Q5 m; T6 m! k) F
*/4 s$ M, U. ^+ K+ [% U6 X
functionname()publicviewreturns(string){- v4 |& ?6 x! h# W5 ] L
returnname_;4 c$ ]' }7 Z( k
}8 a# r0 A% z: B
( k3 E7 _, ~2 S$ |3 [
/**
" g) n- X! j) [+ Y5 Y0 e
*@dev获取代币符号
* } U/ s7 F/ C7 o1 [/ `
*@returnstringrepresentingthetokensymbol, z/ C* i* ?/ V& Q) F+ x* d0 ?
; e! c; R! V6 M4 R
*/
functionsymbol()publicviewreturns(string){) S# ^' |% V5 s; X9 H% W
- {/ S7 ]4 x& ?, [1 i
returnsymbol_;9 e* `# @% G0 z! k# r# f
) L Z/ W9 [" D
}
& L! x, q3 P) \, c. G; I+ B
/**
*@dev根据_tokenId返回对应的资源URI! c' C& S V5 a, S3 A3 B$ Y6 S. r
*@dev如果token不存在异常返回空字符串
*@param_tokenIduint256IDofthetokentoquery
! {' d% |* \7 a7 X# Q" b
*/# A& j1 f2 L2 X# T8 h
; K( ~! e. B1 X( g+ Y9 ~
functiontokenURI(uint256_tokenId)publicviewreturns(string){; V" s5 t7 P0 D7 Q3 Q
1 C! W, h' b+ J) V/ J% R) J
require(exists(_tokenId));! I X) N! O+ V/ H
returntokenURIs[_tokenId];) a( N' Q( s$ P% J4 s' H
) [8 h t8 e6 E2 N* B, I! p! J; M
}0 b1 {$ x3 D3 T/ B1 T% b Y
* o8 _2 g* l$ T+ k J9 L
/**0 j `& Y: R) c! z7 T6 a* b
4 [% G+ J! _& ]9 g' m5 f L
*@dev获取tokenid通过给定的token列表中的索引
*@param_owneraddressowningthetokenslisttobeaccessed
*@param_indexuint256representingtheindextobeaccessedoftherequestedtokenslist1 Q& }' o2 g$ k
*@returnuint256tokenIDatthegivenindexofthetokenslistownedbytherequestedaddress- t! R5 _0 T* I' [
& e6 R# a% X# n/ h
*/% W# W0 H! J0 u$ S) e
functiontokenOfOwnerByIndex(
address_owner,
: S4 ~8 a( Y3 y, L( G j0 f
uint256_index
+ S% N; \4 d% X) U, G
)
public
5 j8 S5 l$ _2 |: b% m" p
view
0 _0 Y8 A# x; `4 L
returns(uint256)% W2 |. b8 N P. Q) J* a2 I5 k
{
require(_index+ Q4 F* J. V8 T; P
ERC721Token实现了完整的ERC721标准,在继承了ERC721BasicToken的基础上增加了一些token的操作,主要在包括token的元数据,资源URI,增发销毁,还有就是token索引的映射关系。对于具体实现我们根据实际情况通过继承ERC721BasicToken或者ERC721Token来添加自己的业务逻辑。2 D: Y, w4 M' F5 v; W0 S+ W! z
OpenZeppelinERC721源码分析到这里就结束了。
成为第一个吐槽的人