Contract-Design
為什麼在執行 transfer() 後檢查 require()
來自 OpenZeppelin 的 ERC721 合約:
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); }
在上面的程式碼中,
require(...)
伺服器_transfer()
無論如何都會轉移令牌的目的是什麼?我想詳細說明為什麼使用這種方法。此外,如果
to
是契約,則_checkOnERC721Received
始終返回 true。這意味著什麼?是否有任何乙太坊合約兼容以安全地開箱即用地持有 ERC721,即使沒有實施ERC721Receiver
?
EIP-721建議使用一個名為的可選介面
ERC721TokenReceiver
:/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. interface ERC721TokenReceiver { /// @notice Handle the receipt of an NFT /// @dev The ERC721 smart contract calls this function on the recipient /// after a `transfer`. This function MAY throw to revert and reject the /// transfer. Return of other than the magic value MUST result in the /// transaction being reverted. /// Note: the contract address is always the message sender. /// @param _operator The address which called `safeTransferFrom` function /// @param _from The address which previously owned the token /// @param _tokenId The NFT identifier which is being transferred /// @param _data Additional data with no specified format /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` /// unless throwing function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4); }
這個介面只有一個名為的函式
onERC721Received
,它返回一個等於 的常量值bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
。您可以在此處找到實施範例:https ://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/utils/ERC721Holder.sol 。這個界面的目的是什麼?
如果有人將代幣發送到不支持 ERC721 標準的智能合約,NFT 可能會永遠失去。
EIP-721 實施了一項
safeTransferFrom
功能來防止這種不良情況。這個方法做的事情基本上和 一樣,只是它在執行結束時transferFrom
呼叫了一個私有函式。_checkOnERC721Received
如何
_checkOnERC721Received
工作?
_checkOnERC721Received
具有以下行為:
- 如果接收地址
to
是 EOA,則返回 true- 如果接收地址
to
是智能合約,它將呼叫合約onERC721Received
上面看到的方法並檢查結果:
- 如果合約返回魔法值,則
_checkOnERC721Received
返回 true- 否則它返回 false 並且
_safeTransfer
呼叫失敗(因為require
不滿足條件)。為什麼
_checkOnERC721Received
最後呼叫呢?為了尊重 Checks-Effects-Interactions 模式並防止重入攻擊:
_safeTransfer
首先更新 ERC721 合約狀態,然後呼叫外部智能合約。