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