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