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