1

As per erc721 a good practice during transfer of an NFT is to use 'safeTransfer()' which calls:

checkAndCallSafeTransfer

of the

IERC721Receiver

interface.

My question is, when creating a new NFT I dont have any value for 'from' address because the contract is just generating a new token and assigning it to a new owner. Most other contracts I saw use '0x' for 'from' address during creation. My new asset creation would look something like below:

MyAsset memory newAsset = MyAsset({
  ....// attributes
  });
uint256 assetId = xxxx;
require(_to != address(0), "Invalid address for transferFrom");
_assignAssetTo(_to, _assetId); //as per spec 
require(_checkAndCallSafeTransfer(_from, _to, _tokenId, _data));

The sample implementations by zepellin here for IERC721Receiver doesn't do much checking of addresses or anything. So if I do this check its fine. But what about the real world exchange implementations. Do they do the same? Because if any implementation does a check of 'from' not being '0x' my creation method would fail in case the asset is being moved to a contract and not a wallet.

Any thoughts?

  • Here is the relevant code: github.com/0xcert/ethereum-erc721/blob/master/contracts/tokens/… – William Entriken Sep 19 '18 at 14:35
  • @WilliamEntriken Thanks for the pointer, I can see that code doesn't do any check for 'from' address before assignment. But my question was for method checkAndCallSafeTransfer of code implementing IERC721Receiver interface. I don't see either of that in the code you mention. – Shashikant Soni Sep 20 '18 at 6:27
  • Thank you, now I am not sure I fully understand the question. But yes, there isn't any check of the from address. – William Entriken Sep 25 '18 at 20:34
3

Problem statement

We want to know if it is required and/or best practice to call onERC721Received on the receiving contract when creating ("minting") a new token.

Literature review

The ERC-721 standard can tell us if this behavior is required. The relevant text is in the documentation for the ERC721 interface:

/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
///  operator, or the approved address for this NFT. Throws if `_from` is
///  not the current owner. Throws if `_to` is the zero address. Throws if
///  `_tokenId` is not a valid NFT. When transfer is complete, this function
///  checks if `_to` is a smart contract (code size > 0). If so, it calls
///  `onERC721Received` on `_to` and throws if the return value is not
///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;

There is nothing in the standard which requires onERC721Received calls for minting operations. The standard is intentionally quiet on issues relating to creating or destroying tokens.

In the wild

Perhaps Su Squares is the first standards-compliant ERC-721 product. Su Squares creates 10,000 tokens at contract deployment and then vends them for sale to customers. You might consider this similar to a minting process. Su Squares implement the vending process at https://github.com/su-squares/ethereum-contract/blob/master/contracts/SuVending.sol#L11-L24 which uses _transfer, the non-"safe" variant, at https://github.com/su-squares/ethereum-contract/blob/master/contracts/SuNFT.sol#L281-L329.

The last time I checked ("Validating NFT Authenticity". NIFTY Hong Kong. July 25, 2018), Codex Protocol was the second project that properly implemented ERC-721. They implement an internal mint function at https://github.com/codex-protocol/contract.codex-registry/blob/master/contracts/CodexRecordCore.sol. This also does not perform "safe" checking for the recipient.

These two implementations have not used the safe variant, of course you are welcome to do so or not as your application requires.

What if I want to?

Here is an example implementation which inherits from the reference implementation and implements "safe" transfers for creating ("minting") new deeds.

pragma solidity ^0.5.1;

import "https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token.sol";

// Divides Earth's lat/long graticules up for claim. This DOES NOT INCLUDE
// ARTIC REGIONS!
contract AirDropEarthDeeds is NFToken {
    bytes4 internal constant MAGIC_ON_ERC721_RECEIVED = 0x150b7a02;

    // Claim ownership of an area ±0.5 deg N/S and ±0.5 deg W/E of a given
    // coordinate.
    function claimPlot(int256 latitude, uint256 longitude) external {
        require (latitude >= -66 && latitude <= 66);
        require (longitude >= 0 && longitude < 360);

        uint tokenId = uint(180 + latitude) * 10000 + longitude;
        address to = msg.sender;
        _mint(to, tokenId);

        if (to.isContract()) 
        {
            bytes4 retval = ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), tokenId, "");
            require(retval == MAGIC_ON_ERC721_RECEIVED);
        }
    }
}
  • 1
    Thanks a lot for all the code references. As you mentioned correctly the "mint" method definition does not specify if you should call 'safeTransfer' or even 'Transfer'. So I had gone ahead and ignored the Transfer call during minting. Though in our contract we do not pre-mint. For now we are safely able to just assign the new asset to the buyer and thats it. – Shashikant Soni Jan 21 at 9:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.