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