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



