Solidity – How to Use External Contract to Msg.Sender for ERC721 Token

erc-721nftopenzeppelin-contractssolidity

I have Caller and ERC721 contract just like this :

contract SampleToken is ERC721MetadataMintable {
    string private name;
    string private symbol;
    constructor (address _creator, string _name, string _symbol, string _tokenURI)
    ERC721MetadataMintable()
    ERC721Metadata(_name, _symbol) public {
        uint256 _tokenId = 0;
        mintWithTokenURI(_creator, _tokenId, _tokenURI);
    }
}

and I want this Caller to be able to transfer the NFT to the person who is msg.sender. Caller behave like factory contract of FRC721 contract.

contract Caller {
    mapping(uint256 => address) public ERC721Address;
    using Address for address;
    bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba;

    struct ERCs {
        address owner;
    }

    ERCs[] public ercs;

    function create(
        string _name,
        string _symbol,
        string _tokenURI
    ) public returns(bool) {
        ERCs memory _ercs = ERCs({
            owner: msg.sender
        });
        uint256 contractId = ercs.push(_ercs) -1; 
        ERC721Address[contractId] = new SampleToken(msg.sender, _name, _symbol, _tokenURI);
        return true;
    }

    function tokenTransferTo(
        address _to,
        uint256 _contractId,
        uint256 _tokenId,
        bytes _data
    ) public {
        require(msg.sender == ownerAddress(_contractId, _tokenId), "You do not have a right to mint token.");
        SampleToken(ERC721Address[_contractId]).setApprovalForAll(msg.sender, true);
        safeTransferFrom(msg.sender, _to, _contractId, _tokenId, _data);
    }

    function ownerAddress(uint256 _contractId, uint256 _tokenId) public view returns(address) {
        return SampleToken(ERC721Address[_contractId]).ownerOf(_tokenId);
    }

    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _contractId,
        uint256 _tokenId,
        bytes _data
    ) public {
        SampleToken(ERC721Address[_contractId]).transferFrom(_from, _to, _tokenId);
        // solium-disable-next-line arg-overflow
        require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data));
    }

    function checkAndCallSafeTransfer(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes _data
    ) internal returns (bool) {
        if (!_to.isContract()) {
          return true;
        }
        bytes4 retval = ERC721Holder(_to).onERC721Received(address(this), _from, _tokenId, _data);
        return (retval == ERC721_RECEIVED);
    }
}

Every time I try to call tokenTransferTo after calling create() function I get the error message like below. Does anyone knows what's wrong with this?

transact to Caller.tokenTransferTo errored: VM error: revert.
revert  The transaction has been reverted to the initial state.
Note: The constructor should be payable if you send value.  Debug the transaction to get more information. 

Best Answer

I guess there are no current solutions for this situation besides implementing Dapps layer to directly call transferFrom(msg.sender, {Caller.contractAddress}, _tokenid) from ERC721 contract side. Since it's always revert when checking require(_isApprovedOrOwner(msg.sender, tokenId)); part.

https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC721/ERC721.sol

So you should do as follows:

  1. transfer token from msg.sender to caller contract address from ERC721 contract.
  2. transfer token from contract address to msg.sender with owner check of ERC721(address).ownerOf({contractAddress})
Related Topic