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