OpenZeppelin ERC721源码分析
杨远枫冠
发表于 2022-11-25 20:49:28
2498
0
0
2 w) `7 ^6 ?' A! ?1 R8 q3 [! S/ g
它和ERC20有所不同,ERC721最小的单位为1无法再分割,代表独一无二的,针对不可置换的Token的智能合约标准接口。从ERC721标准草案中可以看到,兼容ERC20的方法有4个:name,symbol,totalSupply,balanceOf添加的新方法:ownerOf,takeOwnershipERC721还重写了approve和transfer。
ERC721Basic.sol5 W, I0 `8 Q! E# v( j. j
pragmasolidity^0.4.23;
/**
*@titleERC721标准的基本接口
7 o1 ^8 j/ n8 {- I! s! g* b
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contractERC721Basic{& U" [+ f3 X- _+ G3 K
4 t1 f! F" N& E! p3 N0 y7 e) E3 C+ l
eventTransfer( L, t* h- O0 e8 B, ]+ T1 M
addressindexed_from,' ^1 L6 s5 h& ~* n( v
/ }' q. \9 L% L4 v% h3 k
addressindexed_to,
uint256_tokenId
2 ?% _, o: u) g$ F! s
);( T0 X7 t# B, J; W# Y5 i
$ ]9 D: \3 t4 Q1 P
eventApproval(# X* ?0 A, x: t% O0 d: U h
5 n- C* l/ E% D0 c) l
addressindexed_owner,; q" K/ C* M( q- |: h
addressindexed_approved,
uint256_tokenId
);1 P* d9 v1 p" @: Y+ w& L
eventApprovalForAll(3 E$ h# e, t+ D% d. g$ C. L
addressindexed_owner,+ f: l, S: q+ w7 l! Z+ r4 C4 C
addressindexed_operator,
bool_approved: e6 Z( M& z9 E# J
+ L; V) X" ~8 c, u) \8 K' t
);1 d A2 }( c- {* O M
# F0 c) c/ d" }9 S$ G& Q" ^& Z
functionbalanceOf(address_owner)publicviewreturns(uint256_balance);
, K, ^& B1 V" j& k$ x, }
functionownerOf(uint256_tokenId)publicviewreturns(address_owner);
functionexists(uint256_tokenId)publicviewreturns(bool_exists);) I5 Z3 m8 E$ G5 I5 J1 V/ G
functionapprove(address_to,uint256_tokenId)public;6 M+ ] |) i+ s6 d
functiongetApproved(uint256_tokenId), S/ \" F, D" R, h \! d
publicviewreturns(address_operator);
functionsetApprovalForAll(address_operator,bool_approved)public;+ ^7 j% M+ |9 a% V4 v
functionisApprovedForAll(address_owner,address_operator)
publicviewreturns(bool);& B7 }7 R; m( ? p; R
functiontransferFrom(address_from,address_to,uint256_tokenId)public;, C H. a9 c9 @1 l' E& O
0 Y% i) \3 G4 t
functionsafeTransferFrom(address_from,address_to,uint256_tokenId)0 ?# F/ e' M/ D' }7 _9 H3 U
public;" `+ J/ l m* L0 H! @4 l
functionsafeTransferFrom(' V; C: i$ p; r" \( O% L. W
% p" G0 s. q5 E3 ?: D: { H
address_from, H2 c1 B2 Q9 j/ Y. i
address_to,
- t3 R0 Y, O7 D: H
uint256_tokenId,
bytes_data9 n; ^+ K" n; n/ {# h/ C& {
# z8 i7 Q- \! D) z
): a6 Z1 |/ r0 q* Q3 N; J
( T/ z* W3 r! H( v3 } p' O( |
public;
}
, I# D# e- D# f6 a! J( M
ERC721Basic合约定义了基本的接口方法:8 P9 [/ D% Z/ n* i7 t/ b& ]! V `% b* c
balanceOf返回_owner的代币数量3 m9 u. f9 i3 U2 f- H
: i( ~1 \: x0 K% f
ownerOf根据_tokenId返回代币持有者address9 Y8 D. k0 r! n; Z
$ V* k3 f: l* D
exists_tokenId是否存在( T+ D% `- G5 u3 j
) C( u+ u2 |2 b. v7 `
approve授权_tokenId给地址to
% {9 v: ^( N* R, m0 l6 a
getApproved查询_tokenId的授权人_operatoraddress
6 ^1 i/ {. S9 A
setApprovalForAll授权_operator具有所有代币的控制权- F& G; y0 c- x: |, O
, S' J5 F: `8 o: C! A
isApprovedForAll
transferFrom转移代币所有权
safeTransferFrom转移代币所有权8 L% _5 L" R5 P' ^4 v# N
{5 y0 |1 u, \4 e7 b5 y. ~
同时还定义了TransferApprovalApprovalForAll在后面的ERC721实现的代码中再来看事件的触发。7 {* b4 c7 b; }. p1 A" d* t% p
ERC721.sol
) e e- _7 W% ~: P" t
pragmasolidity^0.4.23;
import"./ERC721Basic.sol";' c, |# @5 r% b1 p4 f3 N% s
. ?5 c5 k% p. J4 m* W1 e- g8 T" }
/**; e5 y1 L/ f. a; B& {
*@titleERC-721标准的基本接口,可选的枚举扩展
* `" O( [' V1 D% G' |
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md% Y) [, q( N/ s
*/* N2 e" a2 G' R2 k$ O2 A6 d
contractERC721EnumerableisERC721Basic{$ B. D6 o+ p+ L- j0 \
$ \1 J, ~2 N% V- Q' T3 Z
functiontotalSupply()publicviewreturns(uint256);7 i( ?1 X9 N5 x5 \# Y
' u& k0 p* @$ ^- h
functiontokenOfOwnerByIndex(' _. s( n/ I: s$ j2 X
address_owner,
uint256_index5 @% j! w0 W7 ?/ ^8 H3 A* X3 J3 H
); O. M! f3 M) G' {0 n
public
view
returns(uint256_tokenId);9 E* b1 i# o' ~ X) C
) ?" k( h2 [$ T1 s: [
functiontokenByIndex(uint256_index)publicviewreturns(uint256);+ x- w: n+ U. L+ k8 L; ?' y8 B
}
/**+ {2 Q0 j& W+ M% W- F9 C0 p+ C8 W
9 k/ d6 h% ?4 d4 W9 i( l
*@titleERC-721ERC-721标准的基本接口,可选的元数据扩展
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md w$ R! _8 y( u+ K+ n$ q2 d
" H- |3 k- N# V
*/2 [4 y6 O4 }/ ^: v: p" K
6 |! U* U' G; c* l9 [ `& M1 f! t
contractERC721MetadataisERC721Basic{! T) g) }4 |- L2 a& L, S( b
functionname()publicviewreturns(string_name); M$ g1 x. a. U5 R
functionsymbol()publicviewreturns(string_symbol);
2 J+ u0 _4 B: W6 S+ Q* R% p/ J5 ?: z
functiontokenURI(uint256_tokenId)publicviewreturns(string);! \& O7 T. g9 C" O
}& n" r' Y% q _0 @
5 s" n( q. W! X. k' M
/** {8 v6 y" I/ A3 V" |5 ?
*@titleERC-721标准的基本接口,完整实现接口
* E& \8 f& v* X4 h
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
! x/ D( Y$ v% p
contractERC721isERC721Basic,ERC721Enumerable,ERC721Metadata{
# t4 P p1 z( m
}
ERC721合约继承了ERC721Basic的基础上,添加枚举和元数据的扩展。% s' P, g( \/ h& A, Q$ @6 J* i
0 ?+ r. f3 I* |, Z+ i
ERC721Enumerable枚举扩展可以使代币更具有可访问性:1 {4 a o7 M6 c8 n2 a9 ^
* c5 M8 B' W% D/ w
totalSupply返回代币总量/ N' ?! ?+ G6 ^ G
tokenOfOwnerByIndex通过_owner所有者地址和索引值返回所有者代币列表中的_tokenId+ }9 ]/ f- Q1 U7 u* I
" g5 \5 {" x% J
tokenByIndex通过索引值返回tokenId
# J3 \0 {* H9 v& n5 ^
ERC721Metadata元数据扩展哦用来描述合约元信息' I& p0 m v$ S$ b5 z! r% B
~& K' K, s! c1 k. D0 D' t
name返回合约名字7 I' Y# I1 t. U
* x) C: `" ?* e6 n
symbol返回代币符号) A7 a! r* a6 l# e; u$ v
tokenURI返回_tokenId对应的资源URI
ERC721BasicToken) J% j, L9 V9 S/ G
& y& l# e8 r0 K- K: @6 s: L
ERC721BasicToken
& C8 f+ C, H$ @- @
pragmasolidity^0.4.23;* V2 F+ _9 |/ T3 I! E/ @+ h& J2 _5 m
4 e: m. S% X" C' X# ]+ @' q, N% H
import"./ERC721Basic.sol";' k8 D, P1 T- n) U7 w
+ o& |. H3 w3 t: W6 _. |: Y. R
import"./ERC721Receiver.sol";
import"../../math/SafeMath.sol";" l& V4 u3 \$ _8 H# D* |& e
, ~1 U8 Y8 I# o
import"../../AddressUtils.sol";3 s6 e! T% ^6 h0 _
% `+ Q1 e' R6 E. g- `
/**0 A$ A* i8 F( q4 T v0 ^
*@titleERC721标准基本实现
. P3 ~" i* u+ d
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md# X; R/ P% l! {7 ~1 a' ?1 j6 Q
# B) i. f9 g( X; _. i a- H
*/: X' z4 |4 ^+ X3 x# w0 |
. K0 A9 D; P9 O" O, y, M
contractERC721BasicTokenisERC721Basic{
usingSafeMathforuint256;) i8 u% _0 I4 B% ], Y; M, l1 n6 ^
9 ^! H) p P7 Q" {: j# g ?& Z T
usingAddressUtilsforaddress;
. H+ p) a8 e' J& t& Q& M
//Equalsto`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`: N' F& A3 _5 C+ B0 N! p" |
$ R2 B; r% |" j' h
//whichcanbealsoobtainedas`ERC721Receiver(0).onERC721Received.selector`% z. x# F) B6 e' ^: P8 O6 M9 Z n
bytes4constantERC721_RECEIVED=0xf0b9e5ba;/ k$ R! [% d2 r
//tokenID到持有人owner的映射
4 ]# j$ T( d8 \
mapping(uint256=>address)internaltokenOwner;
//tokenID到授权地址address的映射# S, f8 T7 m8 c5 d% M. a9 z+ D
mapping(uint256=>address)internaltokenApprovals;
//持有人到持有的token数量的映射
. i: Q9 Y3 o( M3 l. n; k
mapping(address=>uint256)internalownedTokensCount;
//持有人到操作人授权的映射
( w, M9 z" A! s2 O! O; n
mapping(address=>mapping(address=>bool))internaloperatorApprovals;6 q( d, N* L1 I L
, ~, v2 C. Q, s4 J: z# r
/**
*@dev确保msg.sender是tokenId的持有人( F. R- n. k2 ?
0 }$ @. K+ E2 e, J" q
*@param_tokenIduint256IDofthetokentovalidateitsownershipbelongstomsg.sender5 D9 L b% A1 ?8 E* {! R0 @ G, {+ Z
*/3 a5 r! L1 ^/ Q9 d
modifieronlyOwnerOf(uint256_tokenId){& T* d1 v# d7 w* A* y
require(ownerOf(_tokenId)==msg.sender);6 ^5 |$ _) Z5 Y
* x+ n' {3 E0 H. u
_;* d3 ] g+ F/ C- u/ i
5 F& z3 r0 f7 {2 ]( w: s
}/ I! H8 ]. @+ ~6 r9 ]$ t8 o0 C. J" V
: ^9 ?5 {5 i8 F) w9 i
/**
4 i2 A3 n2 v9 Z1 T
*@dev通过检查msg.sender是否是代币的持有人,被授权或者操作人来确保msg.sender可以交易一个token
) G, G% C" T' D6 \# ~( z# p
*@param_tokenIduint256IDofthetokentovalidate @* u5 q; b" ]
0 x. ^: q$ g* Y* x. S& ~
*/
9 p$ j: a; E' `! ^
modifiercanTransfer(uint256_tokenId){# c6 @# B* J: ]) j
A* m8 r1 B2 |. O( @8 m7 K6 U& W0 J
require(isApprovedOrOwner(msg.sender,_tokenId));
_;
}
/**
*@dev获取持有者的代币总数
. ~- X- }, Y5 t2 ^
*@param_owneraddresstoquerythebalanceof. {% ^4 P- D( L1 _
*@returnuint256representingtheamountownedbythepassedaddress( F. j( f) d* k) T8 V
*/6 L2 ~8 D0 }6 n1 y8 n
3 F( [/ B) K6 ~. d: Y8 @. g# ^; P3 _
functionbalanceOf(address_owner)publicviewreturns(uint256){0 d$ s: q( W( B3 K. P. E
* m7 a. p! g- i b( G# V
require(_owner!=address(0));
& n+ b! u3 Q- G" y( k3 h; p) s4 z
returnownedTokensCount[_owner];) r$ K4 Q" Z) h9 L# X+ X2 u; C
+ Y% W8 P5 u/ H: V6 q1 s9 z8 W
}
/**
*@dev根据tokenID获取持有者1 K5 Y8 {" S% \9 Q% M8 R+ \
*@param_tokenIduint256IDofthetokentoquerytheownerof
2 q6 B: O8 J7 O1 `
*@returnowneraddresscurrentlymarkedastheownerofthegiventokenID+ Y5 V2 t* H1 \: P7 ^; p4 \2 A
9 U6 R4 v7 K9 }$ z: z* D
*/
# g! {( D) i! g& I2 Q. m% _1 @( q
functionownerOf(uint256_tokenId)publicviewreturns(address){* n! l }+ K: c
addressowner=tokenOwner[_tokenId];
5 u" ^' p+ j0 f( ?# `( s4 k1 v
require(owner!=address(0));/ V- B! x7 _7 V: y" I
returnowner;& q1 {* c8 V" X8 _0 {
}& b# a$ i; K* y- ]
/**
6 E8 g- A1 j/ P- A
*@dev指定的token是否存在
' v% c/ @$ e+ F( j- j. `
*@param_tokenIduint256IDofthetokentoquerytheexistenceof
2 O! }$ x4 f* I8 b$ @' {% f) v+ T
*@returnwhetherthetokenexists4 Y/ Q; n- w5 A+ }
*/
* K# T( M2 C6 H& Y' U; l
functionexists(uint256_tokenId)publicviewreturns(bool){9 V' h% k5 ^$ b1 ] }
addressowner=tokenOwner[_tokenId];
8 b$ L# {0 o; `! g0 Q" X! L
returnowner!=address(0);7 G$ g; `1 L! X1 H/ \
/ x0 k8 M. B/ K" w; ^7 R
}
( ]7 K. B8 M5 u3 L2 k& Q+ }- Z5 n
/**
" r/ i! E# M7 W9 d
*@dev批准另一个人address来交易指定的代币
*@dev0address表示没有授权的地址' z$ v2 Z0 @1 D7 ~) h
5 g5 x" F5 d3 S( h( _( P5 }0 A
*@dev给定的时间内,一个token只能有一个批准的地址7 o; k) c9 D! K) `$ e
*@dev只有token的持有者或者授权的操作人才可以调用
* K/ y* p2 K9 Y
*@param_toaddresstobeapprovedforthegiventokenID
*@param_tokenIduint256IDofthetokentobeapproved6 c( e. k7 B+ D2 S, |2 X, o5 @
*/
functionapprove(address_to,uint256_tokenId)public{
0 f9 m' O" C" C
addressowner=ownerOf(_tokenId);
require(_to!=owner);
require(msg.sender==owner||isApprovedForAll(owner,msg.sender));4 a P4 H. Q% j
9 P- G, h1 i& \4 R4 q, \9 A- u
if(getApproved(_tokenId)!=address(0)||_to!=address(0)){
tokenApprovals[_tokenId]=_to;6 b9 O' Q6 i0 V; ?0 W- j
emitApproval(owner,_to,_tokenId);' C, B9 V P F9 f0 a( z4 w
$ M' h2 Q3 }7 }4 [$ r6 ]
}
}
/**
4 m& N) S' |; t
*@dev获取token被授权的地址,如果没有设置地址则为08 d2 S2 d+ G) ~* g) {2 q
*@param_tokenIduint256IDofthetokentoquerytheapprovalof
*@returnaddresscurrentlyapprovedforthegiventokenID# E; ~1 H( X" n) P
*/
6 k( L! O4 g/ f! i* G
functiongetApproved(uint256_tokenId)publicviewreturns(address){2 |) R3 V, o5 a5 B" `
( w y" I4 x+ d% b! d6 L+ s
returntokenApprovals[_tokenId];
}% {$ Z2 a8 N4 i3 p
/**
/ k3 M8 B. U/ a: e! q8 B7 v
*@dev设置或者取消对操作人的授权% y- _+ b8 v6 f! l
*@dev一个操作人可以代表他们转让发送者的所有token( I+ z8 h1 v; o4 I
*@param_tooperatoraddresstosettheapproval/ p( k1 @' O" k9 J
*@param_approvedrepresentingthestatusoftheapprovaltobeset, k) T' s; Q6 r# g$ x6 R
*/1 | `& |5 a1 r9 F% f# C
functionsetApprovalForAll(address_to,bool_approved)public{- m% O8 U6 R* c) a2 ]' U
* J+ |6 s: a z; d, B0 W) W5 A1 k8 b
require(_to!=msg.sender);" r- {$ l! S& u: N( O
4 Y, ]' U% J( W2 K
operatorApprovals[msg.sender][_to]=_approved;
- k" ^& ~) Z0 ?' a p! B* C
emitApprovalForAll(msg.sender,_to,_approved);
+ A1 K) U8 K: R. O) n
}
/**8 s2 ^2 _ C7 Y. o' f% ~! \6 [
*@dev查询是否操作人被指定的持有者授权
*@param_owner要查询的授权人地址
*@param_operator要查询的授权操作人地址
*@returnboolwhetherthegivenoperatorisapprovedbythegivenowner
*/
; r/ ~! m/ Q$ t' M
functionisApprovedForAll(9 i/ u8 M5 Y+ K* r" h
0 w( F+ j: Z1 P; V- t# a
address_owner,
address_operator8 ^1 J8 N4 l- a% E3 M0 U0 I
2 h( i/ ?' M) \
)5 o$ t, E {' r4 j; T
- J( s& v$ M7 U0 e F
public
view- Q2 |! I7 m$ h" @3 Q/ h, r1 ^
returns(bool)
( U9 F7 `* k! L. H( A% S
{# N8 K6 k' q" b* F% R+ R
returnoperatorApprovals[_owner][_operator];( G& X) P: H$ ]. T' u9 _ `
' f _/ V! F7 E4 |$ b4 J: W0 m
}
1 y9 H y' R. O/ V1 N; R/ X
/**+ @4 Q' \# x3 ]
) b$ S1 H* J$ p. [, y
*@dev将指定的token所有权转移给另外一个地址
& e, s! Y% |3 z9 d0 J
*@dev不鼓励使用这个方法,尽量使用`safeTransferFrom`
*@dev要求msg.sender必须为所有者,已授权或者操作人0 Z+ r6 M' X c+ i8 j+ u$ i6 A
*@param_fromcurrentownerofthetoken8 d$ `8 Q4 }% y
*@param_toaddresstoreceivetheownershipofthegiventokenID4 g* j! f( B: c: P" @
*@param_tokenIduint256IDofthetokentobetransferred6 O/ X) \7 X4 M. T; ]" n
*/
, t% V* `8 C/ y) D
functiontransferFrom(
address_from,
address_to,' J2 L: s( ^" J/ [, I( H/ D% w
uint256_tokenId
% i8 ^8 ?/ d( V& g- Q* `5 Q
)" d/ a7 M* w* q/ l3 j' ^+ U
; k- k3 y- `* F5 Y
public
: {/ n- F. ?) \; i* `
canTransfer(_tokenId)- H$ b0 r# A! q
1 R% @' f! u/ [
{* \1 q# \7 _) w% v
; E. C0 j) X! E9 o
require(_from!=address(0));
" G( y' K) O7 z+ h- M
require(_to!=address(0));
clearApproval(_from,_tokenId);
" L; E" x' ?. H% O% z; [. [2 G
removeTokenFrom(_from,_tokenId);
addTokenTo(_to,_tokenId);; i: x6 V7 n7 d1 R+ m
* t1 k' N( h/ c, v4 r
emitTransfer(_from,_to,_tokenId);
}( G1 ]/ s$ p( X8 u3 W
7 c+ B- W8 l4 y* ?" i
/*** Y* E: n& r& ?' n
4 o- H+ u$ Q0 T L( G+ ~2 b
*@dev更安全的方法,将指定的token所有权转移给另外一个地址# M. q+ |# C8 b- g5 z* L5 P
- v H& c" p1 O
*@dev如果目标地址是一个合约,必须实现`onERC721Received`,这个要求安全交易并返回值
`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`;否则交易被还原0 ~1 j. C S5 | D ?& C) `
5 X9 D0 l X& _" F- Z, G
*@dev要求msg.sender必须为所有者,已授权或者操作人
3 x3 }0 c9 {1 e/ ?. ^1 M- z
*@param_fromcurrentownerofthetoken1 T& ^" Y# N. w& o4 h
*@param_toaddresstoreceivetheownershipofthegiventokenID
9 v) C+ j1 i8 v2 |( Q9 U5 `* _ k
*@param_tokenIduint256IDofthetokentobetransferred$ Y6 d) Y% ^: D1 W' ~( i+ r# H1 x
*/
! @; _ o I% t) h9 f' e* W
functionsafeTransferFrom(
address_from,$ v {! z' G+ {. j. {/ D3 F
8 g* e% K$ m/ F: ~
address_to,- F, z% |3 E7 ^& W4 C1 z
uint256_tokenId! F1 M, T' G/ O! \# h
)) S i5 ]& ?) o$ h- _' R F
public
canTransfer(_tokenId)8 Y d. @9 H8 q9 `2 D+ V i/ U6 I
{
( J$ ]7 R Q# |- J
safeTransferFrom(_from,_to,_tokenId,"");' E& ~( R# u* V
# H7 x* Q+ N2 O, A' T0 J6 O! w
}
3 m4 g% c8 i/ q, B! n* o
/**5 x% O0 \6 _2 p0 F# a, G/ W
+ x2 B# J1 j/ j3 N: L
*@dev更安全的方法,将指定的token所有权转移给另外一个地址+ e& a$ J* ]& V" H4 a. Q1 I; h
; V% Q6 {+ E! A5 J
*@dev如果目标地址是一个合约,必须实现`onERC721Received`,这个要求安全交易并返回值9 q. \) Z* ], E) [
`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`;否则交易被还原
*@dev要求msg.sender必须为所有者,已授权或者操作人5 C& B, S6 {1 G
% X8 O. o# I( g) I3 y
*@param_fromcurrentownerofthetoken7 E5 m6 ?. k, b# Y/ x- [
1 ~; e _1 Z, q& f) r
*@param_toaddresstoreceivetheownershipofthegiventokenID4 R1 s1 M3 Y$ z; C; _) z+ K3 z
/ S* M9 R6 G: F1 H5 z4 ]
*@param_tokenIduint256IDofthetokentobetransferred
* G) Q2 O" b0 b; Y- U
*@param_databytesdatatosendalongwithasafetransfercheck6 q9 X1 e9 w( x( c8 D8 a5 f P/ _
*/( W+ w5 Z- Z) R6 l
& n' ]4 Y+ ~1 f3 ~9 J
functionsafeTransferFrom(
address_from,
address_to,
$ L4 r) U* Z* t, E" |6 m% R( X
uint256_tokenId,- a+ B7 w' }) \
bytes_data7 ^7 ]( n% b0 I) b9 T. l% W1 f$ j
). }2 l/ D+ M) |% R
public0 \7 S# C% A% C0 k: I1 k7 m
/ M ^ ~ `4 m: J6 J. p3 N
canTransfer(_tokenId)- l5 Q8 N! y' m* F
{
transferFrom(_from,_to,_tokenId);, M' m" u6 G: U8 B1 u$ ]( I0 {
( c( |* O8 U4 |& i# J' v' F
require(checkAndCallSafeTransfer(_from,_to,_tokenId,_data));
}( x+ o) O, v$ V( d# g6 w
/**) [6 j* A5 h- Q
*@dev返回给定的spender是否可以交易一个给定的token
*@param_spenderaddressofthespendertoquery T0 c7 v: L9 Y8 a& [0 Y( x
1 X* c3 j4 h* @* @( M4 T" _
*@param_tokenIduint256IDofthetokentobetransferred
" R# K: P& e9 T" X
*@returnboolwhetherthemsg.senderisapprovedforthegiventokenID,$ Y5 p6 V" l7 ?8 m
5 r, s- L: x( F2 [& ?/ t
*isanoperatoroftheowner,oristheownerofthetoken
3 q. v8 n( {- w5 M+ ^
*/6 W3 e5 C/ l' R# c! R; z5 S
L: g. k$ Z/ A% S2 J- Y0 i
functionisApprovedOrOwner(5 J. u9 m' v4 L
+ S# B) [. E, N K7 T
address_spender,* U8 ?# N3 l" H: l
uint256_tokenId
)% }6 C2 I$ O" ^" P$ S% J
internal
view0 @! t. o& I! V3 [3 L
returns(bool)
{
addressowner=ownerOf(_tokenId);' s# D0 c, g {! f
return(. s' a: a. ^. L9 y- S
_spender==owner||: ?, c/ x- u9 F0 ]; F1 T
getApproved(_tokenId)==_spender||
isApprovedForAll(owner,_spender)
);
}6 }: g8 S, Z6 ^) X$ D
$ `) m- `! y5 n$ \3 C3 K
/**2 U$ n( [! s d- N5 _- _" A# P% j
*@dev增发一个新token的内部方法
*@dev如果增发的token已经存在则撤销
*@param_toTheaddressthatwillownthemintedtoken# _, G, Q& _, B/ ^) _- J
' [" I: h: Z, O5 c7 q" T
*@param_tokenIduint256IDofthetokentobemintedbythemsg.sender I: E( K' H& A' a) T
*/1 |4 b& K$ R) v" I* x9 j
% X+ o e# t7 B0 ~* t
function_mint(address_to,uint256_tokenId)internal{1 S, B W# i: K+ j' s
require(_to!=address(0));8 {, @6 A! b0 c6 ~$ l4 K( z
/ P: e! |# s4 e7 {8 z+ @+ w% K5 b: F3 C
addTokenTo(_to,_tokenId);, {9 B. N2 m0 ?+ V7 s2 c5 G! k
emitTransfer(address(0),_to,_tokenId); J* ?, `+ L J, K% B( J
}5 G+ u" n k O) z, ]/ b9 I
% d. [4 a" z9 d4 D
/**2 {% k& f {0 H6 ^% D
*@dev销毁一个token的内部方法/ W3 O: D, V* [! f- S2 _
*@dev如果token不存在则撤销
# _0 [( S2 t6 m" P6 t, @
*@param_tokenIduint256IDofthetokenbeingburnedbythemsg.sender* i5 Z& t3 k- |- o
& B {& |; {0 e+ Y; c
*// A, ^. D; H7 X( T% s+ l* u
) S! J$ d: A1 Z, q# {
function_burn(address_owner,uint256_tokenId)internal{
! [3 X" ~% y8 q+ u
clearApproval(_owner,_tokenId);& l1 t, A; @. g, R4 J# B
removeTokenFrom(_owner,_tokenId);& J- _) g% t3 f
+ I4 Z' X+ j3 {+ I
emitTransfer(_owner,address(0),_tokenId);
}
2 _% t s5 _ S$ F! y
/**
*@dev清除当前的给定token的授权,内部方法
4 N% m, E9 z9 c% _1 q* k
*@dev如果给定地址不是token的持有者则撤销8 }5 S4 a. Z1 {% i# o
*@param_ownerownerofthetoken* u9 h5 ^7 c/ \
5 u5 A% x6 N) X' f h
*@param_tokenIduint256IDofthetokentobetransferred; n4 V/ |) Q) @* n+ ]- U
- B" x0 R7 `. x7 ?; j+ [7 G- L
*/9 [3 G+ ~* ~2 f* ]# m
functionclearApproval(address_owner,uint256_tokenId)internal{
require(ownerOf(_tokenId)==_owner);2 N/ i: b* o" u% N$ F1 t
5 a8 ~" G, w2 I; p
if(tokenApprovals[_tokenId]!=address(0)){
9 C1 @* O( c9 {
tokenApprovals[_tokenId]=address(0);
emitApproval(_owner,address(0),_tokenId);! j8 H, S: [* q5 w5 k
}8 ?4 k) D* J0 i0 K
}5 {8 |; f: R* c& |6 y
/**
& ?: \! B! ~0 H% Y
*@dev内部方法,将给定的token添加到给定地址列表中& ^" ^0 k. l; w( P
% N9 F+ T* t$ m5 N* k- [
*@param_toaddress指定token的新所有者
: I) K3 O* s1 Q$ C: J* M
*@param_tokenIduint256IDofthetokentobeaddedtothetokenslistofthegivenaddress! {: d$ b, |% B8 b0 V
*/
functionaddTokenTo(address_to,uint256_tokenId)internal{
& \/ a1 I. U& P' w8 T. w4 A
require(tokenOwner[_tokenId]==address(0));- Y9 B) E: }. P
tokenOwner[_tokenId]=_to;
7 p1 U* N8 h+ ~7 b! C x
ownedTokensCount[_to]=ownedTokensCount[_to].add(1);
}
/**
8 o" ~6 Z" E: s) D+ n9 [4 s
*@dev内部方法,将给定的token从地址列表中移除# Q& e, |2 A* |7 {% J
5 {. |, e& W9 ^6 y* E4 k
*@param_fromaddress给定token的之前持有中地址2 ?5 N! ^: Y! s/ {' |
*@param_tokenIduint256IDofthetokentoberemovedfromthetokenslistofthegivenaddress, E- A- E# _- m5 X2 {+ `, Q, o
& t! w$ G3 @ X! _- R
*/
4 s4 d4 f8 y% f J7 m7 Q! H
functionremoveTokenFrom(address_from,uint256_tokenId)internal{ j5 J$ t+ s5 ]' ^4 g, c$ L
require(ownerOf(_tokenId)==_from);
( \2 F- ?0 h5 U" H, E2 Q! j
ownedTokensCount[_from]=ownedTokensCount[_from].sub(1);2 p; H2 [6 x. |! k
tokenOwner[_tokenId]=address(0);9 p( S4 t4 N3 h* J0 K% m
}; M6 ?' f% Z+ a
0 [2 K/ ^% B* e5 @ [# I
/**& c' c: T6 A9 B3 y6 z- j5 G
*@dev内部函数,调用目标地址上的`onERC721Received`3 |0 G% f5 E& z; l' [
7 F+ K! ^+ i* y$ x
*@dev如果目标地址不是合同则不执行调用
3 k; Q, O/ @# |. Y
*@param_fromaddressrepresentingthepreviousownerofthegiventokenID3 ^' P1 B$ O; u ] t
*@param_totargetaddressthatwillreceivethetokens: m+ L- a' M( d; V& q
6 P) M& L r* C! y: A8 F& s4 \
*@param_tokenIduint256IDofthetokentobetransferred
*@param_databytesoptionaldatatosendalongwiththecall
*@returnwhetherthecallcorrectlyreturnedtheexpectedmagicvalue5 M! h+ K; f8 S+ x
% s# o3 s. h+ j9 J1 \# w
*/
functioncheckAndCallSafeTransfer(' l% N( r7 Z1 f# r! C
address_from,4 c4 U* E( a2 R7 ?
address_to,
uint256_tokenId," X& H5 n2 r) \$ d: t9 f+ \) p
bytes_data) f/ b5 f* A) |
( d' `1 _$ E3 K
)
internal
returns(bool)' V/ Q+ G% M: f& M# [9 r
{) R& l/ E5 a# G5 D
if(!_to.isContract()){
) f; |$ B- `# |- f5 l$ n$ o0 Z
returntrue;4 G, k: H0 Q& U
3 Y# L+ v( V9 T, a3 Q
}
bytes4retval=ERC721Receiver(_to).onERC721Received(( O, y0 @3 b$ I [# @
_from,_tokenId,_data);
( ~ O9 O4 Q* p$ g) Q3 V: T
return(retval==ERC721_RECEIVED);; }# p: t4 _, ?- o6 `3 _( T
}
. T# W& Z' W5 M; F5 G. [1 U! ] ~8 q
}0 d5 p( `# W' z7 |5 T
* Z% w# D; f z- ?
ERC721BasicToken实现了ERC721Basic合约定义的接口方法,主要对token的持有人的一个添加和修改,以及授权和交易的管理,实现了基本的非同质化token的业务逻辑。具体方法实现并不难,就是对映射的公有变量的管理,但是对于权限和安全验证值得关注,比如函数修改器还有require。
ERC721Token.sol
* i0 a1 l! Z, M( c4 O3 B0 z" m
pragmasolidity^0.4.23;
7 ?& p* G1 G( w9 j
import"./ERC721.sol";
import"./ERC721BasicToken.sol";
/**
* H% {* z, s0 w% W
*@title完整ERC721Token
*该实现包括所有ERC721标准必须的和可选的方法,此外还包括使用操作者批准所有功能! ~) g7 D2 y6 t4 @9 X, r& \
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
4 Q' ?3 o+ ^, c2 ]
contractERC721TokenisERC721,ERC721BasicToken{
//代币名称, R& d* i3 {) g8 N) q3 p" p
stringinternalname_;
//代币符号
stringinternalsymbol_;" h6 I, B0 [# }; p" H: H
' R* u- ~. W- J- h0 o
//所有者到所有者拥有的代币列表的映射
mapping(address=>uint256[])internalownedTokens;
' z' o, ?. D; I0 X# V
//所有者代币列表中代币ID到索引的映射
mapping(uint256=>uint256)internalownedTokensIndex;
//保存所有代币ID的数组,用于枚举' K% W, p- U2 u& G
% B& P8 a, M' ]8 K
uint256[]internalallTokens;
) O* `( S) d! U$ k1 O
//allTokens数组中代币ID到索引的映射4 f I7 s" c. T" c! i
7 X/ {( W7 B& E
mapping(uint256=>uint256)internalallTokensIndex;
5 G" d( x% ?" g+ k+ ^
//可选的代币资源URIs映射
mapping(uint256=>string)internaltokenURIs;
5 ?7 f. I, D. Y8 t }( w0 n
/**6 b5 Z+ ]# ~# _ o1 T
*@devConstructorfunction
2 }0 i$ A8 [; O8 j8 ?9 U
*/0 g$ [, R9 b' H- q% o
. M t4 w. k; J
constructor(string_name,string_symbol)public{
name_=_name;
symbol_=_symbol;' Q/ J/ H4 b* M. k0 b; l& }
}
/**
% Q7 V) D2 z3 k! J2 D o$ e
*@dev获取代币名称: C- c, n8 Z& r, I* V, f
*@returnstringrepresentingthetokenname2 ^- E! k& V& V5 |( }$ F9 f
*/" X* D1 Z& p' U0 M$ x/ o5 ?: s# }
functionname()publicviewreturns(string){
o5 J z5 J* |9 c' ^! @( }
returnname_; u; C; k! s' f
}' L4 m0 u4 o9 `8 \! n8 q6 s" a
/**
& b4 O/ Y, |4 Q9 A
*@dev获取代币符号. |# `* ]$ X' q' Y; M7 D- D
J4 n: m0 }* x8 l
*@returnstringrepresentingthetokensymbol
*/2 T, w% I7 m8 {; n2 j S b# W
1 g7 C# q* p& H& T; J1 Q
functionsymbol()publicviewreturns(string){' r8 U' K" M7 Y5 ^
returnsymbol_;! L- F _6 ^7 E( h
7 w3 U* a9 s( S6 K7 u
}
. n9 A5 B' n( b$ {9 {' B
/**
h+ q! }7 c y+ q4 H
*@dev根据_tokenId返回对应的资源URI
*@dev如果token不存在异常返回空字符串. Q& d: C3 X+ z! }) T# J8 C
*@param_tokenIduint256IDofthetokentoquery' y, k! @7 u3 X0 e
4 ]$ T6 E+ D% l( @% H# F
*/; I7 v/ X0 O% V
functiontokenURI(uint256_tokenId)publicviewreturns(string){+ W" D0 _ o" @
. X/ [7 c6 _- Z. A# l- ?) P
require(exists(_tokenId));' u& F. ~- u* p( s2 Y" }, d" h" X
returntokenURIs[_tokenId];
}
~2 @: J/ C( y F& V! D
/**
*@dev获取tokenid通过给定的token列表中的索引! I/ i5 p0 N3 ^5 {+ e4 Q; r6 ?
*@param_owneraddressowningthetokenslisttobeaccessed
! x# P" U9 G. |+ V1 s, L" S$ Q
*@param_indexuint256representingtheindextobeaccessedoftherequestedtokenslist9 o: x; Q4 M! j4 d
*@returnuint256tokenIDatthegivenindexofthetokenslistownedbytherequestedaddress% E& Y, e7 z; M; w
*/
functiontokenOfOwnerByIndex(7 [2 P W" a. C% Y
address_owner,
uint256_index! d9 c2 U/ p: a, m7 F
% F1 K, ?4 U4 w8 b: I
)$ q; {! O3 Z$ s1 d! P
( F0 a v, X$ ~4 s$ ^9 Y: r
public B E7 k1 Q# L
+ D, }+ m6 n" N/ m7 t" H
view
- n" U6 J1 _8 g
returns(uint256)
{
! ]1 \5 R$ B# r" }8 r; k" @: Z
require(_index
- }" u& P9 `( N3 g
ERC721Token实现了完整的ERC721标准,在继承了ERC721BasicToken的基础上增加了一些token的操作,主要在包括token的元数据,资源URI,增发销毁,还有就是token索引的映射关系。对于具体实现我们根据实际情况通过继承ERC721BasicToken或者ERC721Token来添加自己的业务逻辑。4 g7 `8 w9 G/ `! m
- K! \6 g; }1 p7 k) o/ t; ~
OpenZeppelinERC721源码分析到这里就结束了。
成为第一个吐槽的人



