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