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