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