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