I'm studying OpenZeppelin's ERC721 token implementation and I'm having a hard time making sense of ERC721Receiver.sol and ERC721Holder.sol and how they relate to eachother.
In the file ERC721BasicToken.sol only the file ERC721Receiver.sol is imported.
When ERC721Receiver.sol only declares the function onERC721Received
and ERC721Holder.sol is the contract defining it.
You might have to take a quick look at the implementation on GitHub to see what I'm talking about.
It it supposed to be like that?
Or is that a bug in the implementation?
Also what really is the point of having this functionality and is it really necessary?
Including examples would be very nice.
Relevant Code:
ERC721Receiver.sol
pragma solidity ^0.4.23;
/// @title ERC721 token receiver interface
/// @dev Interface for any contract that wants to support safeTransfers from ERC721 asset contracts.
contract ERC721Receiver {
/// @dev Magic value to be returned upon successful reception of an NFT
/// Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`,
/// which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba;
/// @notice Handle the receipt of an NFT
/// @dev The ERC721 smart contract calls this function on the recipient
/// after a `safetransfer`. This function MAY throw to revert and reject the
/// transfer. This function MUST use 50,000 gas or less. Return of other
/// than the magic value MUST result in the transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _from The sending address
/// @param _tokenId The NFT identifier which is being transfered
/// @param _data Additional data with no specified format
/// @return
`bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
function onERC721Received(address _from, uint256 _tokenId, bytes _data) public returns(bytes4);
}
ERC721Holder.sol
pragma solidity ^0.4.23;
import "./ERC721Receiver.sol";
contract ERC721Holder is ERC721Receiver {
function onERC721Received(address, uint256, bytes) public returns(bytes4) {
return ERC721_RECEIVED;
}
}
ERC721BasicToken.sol
pragma solidity ^0.4.23;
import "./ERC721Basic.sol";
import "./ERC721Receiver.sol";
import "./SafeMath.sol";
import "./AddressUtils.sol";
/// @title ERC721 Non-Fungible Token Standard basic implementation
/// @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
contract ERC721BasicToken is ERC721Basic {
.
.
.
// Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
// which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba;
.
.
.
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) public canTransfer(_tokenId) {
transferFrom(_from, _to, _tokenId);
// solium-disable-next-line arg-overflow
require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data));
}
.
.
.
/// @dev Internal function to invoke `onERC721Received` on a target address
/// @dev The call is not executed if the target address is not a contract
/// @param _from address representing the previous owner of the given token ID
/// @param _to target address that will receive the tokens
/// @param _tokenId uint256 ID of the token to be transferred
/// @param _data bytes optional data to send along with the call
/// @return whether the call correctly returned the expected magic value
function checkAndCallSafeTransfer(address _from, address _to, uint256 _tokenId, bytes _data) internal returns (bool) {
if (!_to.isContract()) {
return true;
}
bytes4 retval = ERC721Receiver(_to).onERC721Received(_from, _tokenId, _data);
return (retval == ERC721_RECEIVED);
}
}