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