OpenZeppelin ERC721源码分析
杨远枫冠
post on 2022-11-25 20:49:28
2325
0
0
它和ERC20有所不同,ERC721最小的单位为1无法再分割,代表独一无二的,针对不可置换的Token的智能合约标准接口。从ERC721标准草案中可以看到,兼容ERC20的方法有4个:name,symbol,totalSupply,balanceOf添加的新方法:ownerOf,takeOwnershipERC721还重写了approve和transfer。
ERC721Basic.sol
pragmasolidity^0.4.23;
/**
*@titleERC721标准的基本接口
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contractERC721Basic{
eventTransfer(
addressindexed_from,
addressindexed_to,
uint256_tokenId
);
eventApproval(
addressindexed_owner,
addressindexed_approved,
uint256_tokenId
);
eventApprovalForAll(
addressindexed_owner,
addressindexed_operator,
bool_approved
);
functionbalanceOf(address_owner)publicviewreturns(uint256_balance);
functionownerOf(uint256_tokenId)publicviewreturns(address_owner);
functionexists(uint256_tokenId)publicviewreturns(bool_exists);
functionapprove(address_to,uint256_tokenId)public;
functiongetApproved(uint256_tokenId)
publicviewreturns(address_operator);
functionsetApprovalForAll(address_operator,bool_approved)public;
functionisApprovedForAll(address_owner,address_operator)
publicviewreturns(bool);
functiontransferFrom(address_from,address_to,uint256_tokenId)public;
functionsafeTransferFrom(address_from,address_to,uint256_tokenId)
public;
functionsafeTransferFrom(
address_from,
address_to,
uint256_tokenId,
bytes_data
)
public;
}
ERC721Basic合约定义了基本的接口方法:
balanceOf返回_owner的代币数量
ownerOf根据_tokenId返回代币持有者address
exists_tokenId是否存在
approve授权_tokenId给地址to
getApproved查询_tokenId的授权人_operatoraddress
setApprovalForAll授权_operator具有所有代币的控制权
isApprovedForAll
transferFrom转移代币所有权
safeTransferFrom转移代币所有权
同时还定义了TransferApprovalApprovalForAll在后面的ERC721实现的代码中再来看事件的触发。
ERC721.sol
pragmasolidity^0.4.23;
import"./ERC721Basic.sol";
/**
*@titleERC-721标准的基本接口,可选的枚举扩展
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contractERC721EnumerableisERC721Basic{
functiontotalSupply()publicviewreturns(uint256);
functiontokenOfOwnerByIndex(
address_owner,
uint256_index
)
public
view
returns(uint256_tokenId);
functiontokenByIndex(uint256_index)publicviewreturns(uint256);
}
/**
*@titleERC-721ERC-721标准的基本接口,可选的元数据扩展
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contractERC721MetadataisERC721Basic{
functionname()publicviewreturns(string_name);
functionsymbol()publicviewreturns(string_symbol);
functiontokenURI(uint256_tokenId)publicviewreturns(string);
}
/**
*@titleERC-721标准的基本接口,完整实现接口
*@devSeehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contractERC721isERC721Basic,ERC721Enumerable,ERC721Metadata{
}
ERC721合约继承了ERC721Basic的基础上,添加枚举和元数据的扩展。
ERC721Enumerable枚举扩展可以使代币更具有可访问性:
totalSupply返回代币总量
tokenOfOwnerByIndex通过_owner所有者地址和索引值返回所有者代币列表中的_tokenId
tokenByIndex通过索引值返回tokenId
ERC721Metadata元数据扩展哦用来描述合约元信息
name返回合约名字
symbol返回代币符号
tokenURI返回_tokenId对应的资源URI
ERC721BasicToken
ERC721BasicToken
pragmasolidity^0.4.23;
import"./ERC721Basic.sol";
import"./ERC721Receiver.sol";
import"../../math/SafeMath.sol";
import"../../AddressUtils.sol";
/**
*@titleERC721标准基本实现
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contractERC721BasicTokenisERC721Basic{
usingSafeMathforuint256;
usingAddressUtilsforaddress;
//Equalsto`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
//whichcanbealsoobtainedas`ERC721Receiver(0).onERC721Received.selector`
bytes4constantERC721_RECEIVED=0xf0b9e5ba;
//tokenID到持有人owner的映射
mapping(uint256=>address)internaltokenOwner;
//tokenID到授权地址address的映射
mapping(uint256=>address)internaltokenApprovals;
//持有人到持有的token数量的映射
mapping(address=>uint256)internalownedTokensCount;
//持有人到操作人授权的映射
mapping(address=>mapping(address=>bool))internaloperatorApprovals;
/**
*@dev确保msg.sender是tokenId的持有人
*@param_tokenIduint256IDofthetokentovalidateitsownershipbelongstomsg.sender
*/
modifieronlyOwnerOf(uint256_tokenId){
require(ownerOf(_tokenId)==msg.sender);
_;
}
/**
*@dev通过检查msg.sender是否是代币的持有人,被授权或者操作人来确保msg.sender可以交易一个token
*@param_tokenIduint256IDofthetokentovalidate
*/
modifiercanTransfer(uint256_tokenId){
require(isApprovedOrOwner(msg.sender,_tokenId));
_;
}
/**
*@dev获取持有者的代币总数
*@param_owneraddresstoquerythebalanceof
*@returnuint256representingtheamountownedbythepassedaddress
*/
functionbalanceOf(address_owner)publicviewreturns(uint256){
require(_owner!=address(0));
returnownedTokensCount[_owner];
}
/**
*@dev根据tokenID获取持有者
*@param_tokenIduint256IDofthetokentoquerytheownerof
*@returnowneraddresscurrentlymarkedastheownerofthegiventokenID
*/
functionownerOf(uint256_tokenId)publicviewreturns(address){
addressowner=tokenOwner[_tokenId];
require(owner!=address(0));
returnowner;
}
/**
*@dev指定的token是否存在
*@param_tokenIduint256IDofthetokentoquerytheexistenceof
*@returnwhetherthetokenexists
*/
functionexists(uint256_tokenId)publicviewreturns(bool){
addressowner=tokenOwner[_tokenId];
returnowner!=address(0);
}
/**
*@dev批准另一个人address来交易指定的代币
*@dev0address表示没有授权的地址
*@dev给定的时间内,一个token只能有一个批准的地址
*@dev只有token的持有者或者授权的操作人才可以调用
*@param_toaddresstobeapprovedforthegiventokenID
*@param_tokenIduint256IDofthetokentobeapproved
*/
functionapprove(address_to,uint256_tokenId)public{
addressowner=ownerOf(_tokenId);
require(_to!=owner);
require(msg.sender==owner||isApprovedForAll(owner,msg.sender));
if(getApproved(_tokenId)!=address(0)||_to!=address(0)){
tokenApprovals[_tokenId]=_to;
emitApproval(owner,_to,_tokenId);
}
}
/**
*@dev获取token被授权的地址,如果没有设置地址则为0
*@param_tokenIduint256IDofthetokentoquerytheapprovalof
*@returnaddresscurrentlyapprovedforthegiventokenID
*/
functiongetApproved(uint256_tokenId)publicviewreturns(address){
returntokenApprovals[_tokenId];
}
/**
*@dev设置或者取消对操作人的授权
*@dev一个操作人可以代表他们转让发送者的所有token
*@param_tooperatoraddresstosettheapproval
*@param_approvedrepresentingthestatusoftheapprovaltobeset
*/
functionsetApprovalForAll(address_to,bool_approved)public{
require(_to!=msg.sender);
operatorApprovals[msg.sender][_to]=_approved;
emitApprovalForAll(msg.sender,_to,_approved);
}
/**
*@dev查询是否操作人被指定的持有者授权
*@param_owner要查询的授权人地址
*@param_operator要查询的授权操作人地址
*@returnboolwhetherthegivenoperatorisapprovedbythegivenowner
*/
functionisApprovedForAll(
address_owner,
address_operator
)
public
view
returns(bool)
{
returnoperatorApprovals[_owner][_operator];
}
/**
*@dev将指定的token所有权转移给另外一个地址
*@dev不鼓励使用这个方法,尽量使用`safeTransferFrom`
*@dev要求msg.sender必须为所有者,已授权或者操作人
*@param_fromcurrentownerofthetoken
*@param_toaddresstoreceivetheownershipofthegiventokenID
*@param_tokenIduint256IDofthetokentobetransferred
*/
functiontransferFrom(
address_from,
address_to,
uint256_tokenId
)
public
canTransfer(_tokenId)
{
require(_from!=address(0));
require(_to!=address(0));
clearApproval(_from,_tokenId);
removeTokenFrom(_from,_tokenId);
addTokenTo(_to,_tokenId);
emitTransfer(_from,_to,_tokenId);
}
/**
*@dev更安全的方法,将指定的token所有权转移给另外一个地址
*@dev如果目标地址是一个合约,必须实现`onERC721Received`,这个要求安全交易并返回值
`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`;否则交易被还原
*@dev要求msg.sender必须为所有者,已授权或者操作人
*@param_fromcurrentownerofthetoken
*@param_toaddresstoreceivetheownershipofthegiventokenID
*@param_tokenIduint256IDofthetokentobetransferred
*/
functionsafeTransferFrom(
address_from,
address_to,
uint256_tokenId
)
public
canTransfer(_tokenId)
{
safeTransferFrom(_from,_to,_tokenId,"");
}
/**
*@dev更安全的方法,将指定的token所有权转移给另外一个地址
*@dev如果目标地址是一个合约,必须实现`onERC721Received`,这个要求安全交易并返回值
`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`;否则交易被还原
*@dev要求msg.sender必须为所有者,已授权或者操作人
*@param_fromcurrentownerofthetoken
*@param_toaddresstoreceivetheownershipofthegiventokenID
*@param_tokenIduint256IDofthetokentobetransferred
*@param_databytesdatatosendalongwithasafetransfercheck
*/
functionsafeTransferFrom(
address_from,
address_to,
uint256_tokenId,
bytes_data
)
public
canTransfer(_tokenId)
{
transferFrom(_from,_to,_tokenId);
require(checkAndCallSafeTransfer(_from,_to,_tokenId,_data));
}
/**
*@dev返回给定的spender是否可以交易一个给定的token
*@param_spenderaddressofthespendertoquery
*@param_tokenIduint256IDofthetokentobetransferred
*@returnboolwhetherthemsg.senderisapprovedforthegiventokenID,
*isanoperatoroftheowner,oristheownerofthetoken
*/
functionisApprovedOrOwner(
address_spender,
uint256_tokenId
)
internal
view
returns(bool)
{
addressowner=ownerOf(_tokenId);
return(
_spender==owner||
getApproved(_tokenId)==_spender||
isApprovedForAll(owner,_spender)
);
}
/**
*@dev增发一个新token的内部方法
*@dev如果增发的token已经存在则撤销
*@param_toTheaddressthatwillownthemintedtoken
*@param_tokenIduint256IDofthetokentobemintedbythemsg.sender
*/
function_mint(address_to,uint256_tokenId)internal{
require(_to!=address(0));
addTokenTo(_to,_tokenId);
emitTransfer(address(0),_to,_tokenId);
}
/**
*@dev销毁一个token的内部方法
*@dev如果token不存在则撤销
*@param_tokenIduint256IDofthetokenbeingburnedbythemsg.sender
*/
function_burn(address_owner,uint256_tokenId)internal{
clearApproval(_owner,_tokenId);
removeTokenFrom(_owner,_tokenId);
emitTransfer(_owner,address(0),_tokenId);
}
/**
*@dev清除当前的给定token的授权,内部方法
*@dev如果给定地址不是token的持有者则撤销
*@param_ownerownerofthetoken
*@param_tokenIduint256IDofthetokentobetransferred
*/
functionclearApproval(address_owner,uint256_tokenId)internal{
require(ownerOf(_tokenId)==_owner);
if(tokenApprovals[_tokenId]!=address(0)){
tokenApprovals[_tokenId]=address(0);
emitApproval(_owner,address(0),_tokenId);
}
}
/**
*@dev内部方法,将给定的token添加到给定地址列表中
*@param_toaddress指定token的新所有者
*@param_tokenIduint256IDofthetokentobeaddedtothetokenslistofthegivenaddress
*/
functionaddTokenTo(address_to,uint256_tokenId)internal{
require(tokenOwner[_tokenId]==address(0));
tokenOwner[_tokenId]=_to;
ownedTokensCount[_to]=ownedTokensCount[_to].add(1);
}
/**
*@dev内部方法,将给定的token从地址列表中移除
*@param_fromaddress给定token的之前持有中地址
*@param_tokenIduint256IDofthetokentoberemovedfromthetokenslistofthegivenaddress
*/
functionremoveTokenFrom(address_from,uint256_tokenId)internal{
require(ownerOf(_tokenId)==_from);
ownedTokensCount[_from]=ownedTokensCount[_from].sub(1);
tokenOwner[_tokenId]=address(0);
}
/**
*@dev内部函数,调用目标地址上的`onERC721Received`
*@dev如果目标地址不是合同则不执行调用
*@param_fromaddressrepresentingthepreviousownerofthegiventokenID
*@param_totargetaddressthatwillreceivethetokens
*@param_tokenIduint256IDofthetokentobetransferred
*@param_databytesoptionaldatatosendalongwiththecall
*@returnwhetherthecallcorrectlyreturnedtheexpectedmagicvalue
*/
functioncheckAndCallSafeTransfer(
address_from,
address_to,
uint256_tokenId,
bytes_data
)
internal
returns(bool)
{
if(!_to.isContract()){
returntrue;
}
bytes4retval=ERC721Receiver(_to).onERC721Received(
_from,_tokenId,_data);
return(retval==ERC721_RECEIVED);
}
}
ERC721BasicToken实现了ERC721Basic合约定义的接口方法,主要对token的持有人的一个添加和修改,以及授权和交易的管理,实现了基本的非同质化token的业务逻辑。具体方法实现并不难,就是对映射的公有变量的管理,但是对于权限和安全验证值得关注,比如函数修改器还有require。
ERC721Token.sol
pragmasolidity^0.4.23;
import"./ERC721.sol";
import"./ERC721BasicToken.sol";
/**
*@title完整ERC721Token
*该实现包括所有ERC721标准必须的和可选的方法,此外还包括使用操作者批准所有功能
*@devseehttps://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contractERC721TokenisERC721,ERC721BasicToken{
//代币名称
stringinternalname_;
//代币符号
stringinternalsymbol_;
//所有者到所有者拥有的代币列表的映射
mapping(address=>uint256[])internalownedTokens;
//所有者代币列表中代币ID到索引的映射
mapping(uint256=>uint256)internalownedTokensIndex;
//保存所有代币ID的数组,用于枚举
uint256[]internalallTokens;
//allTokens数组中代币ID到索引的映射
mapping(uint256=>uint256)internalallTokensIndex;
//可选的代币资源URIs映射
mapping(uint256=>string)internaltokenURIs;
/**
*@devConstructorfunction
*/
constructor(string_name,string_symbol)public{
name_=_name;
symbol_=_symbol;
}
/**
*@dev获取代币名称
*@returnstringrepresentingthetokenname
*/
functionname()publicviewreturns(string){
returnname_;
}
/**
*@dev获取代币符号
*@returnstringrepresentingthetokensymbol
*/
functionsymbol()publicviewreturns(string){
returnsymbol_;
}
/**
*@dev根据_tokenId返回对应的资源URI
*@dev如果token不存在异常返回空字符串
*@param_tokenIduint256IDofthetokentoquery
*/
functiontokenURI(uint256_tokenId)publicviewreturns(string){
require(exists(_tokenId));
returntokenURIs[_tokenId];
}
/**
*@dev获取tokenid通过给定的token列表中的索引
*@param_owneraddressowningthetokenslisttobeaccessed
*@param_indexuint256representingtheindextobeaccessedoftherequestedtokenslist
*@returnuint256tokenIDatthegivenindexofthetokenslistownedbytherequestedaddress
*/
functiontokenOfOwnerByIndex(
address_owner,
uint256_index
)
public
view
returns(uint256)
{
require(_index
ERC721Token实现了完整的ERC721标准,在继承了ERC721BasicToken的基础上增加了一些token的操作,主要在包括token的元数据,资源URI,增发销毁,还有就是token索引的映射关系。对于具体实现我们根据实际情况通过继承ERC721BasicToken或者ERC721Token来添加自己的业务逻辑。
OpenZeppelinERC721源码分析到这里就结束了。
BitMere.com is Information release platform,just provides information storage space services.
The opinions expressed are solely those of the author,Does not constitute advice, please treat with caution.
The opinions expressed are solely those of the author,Does not constitute advice, please treat with caution.
Write the first review