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