Approve + safeTransferFrom ERC721 from existing smart contract

I am trying to call ERC721 from another smart contract, while calling function approveAndTransfer ,I am getting “ERC721: transfer caller is not owner nor approved”, not sure reason behind it,

As require(nft.ownerOf(_tokenId) == msg.sender, “NOT OWNER”); is working as expected and not throwing any error, but source of error is _tranfer function which gets called from nft.safeTransferFrom(msg.sender, _to,_tokenId);

not sure what mistake I am making. I have NFT in my wallet, but I am not owner of external smart contract.

    // SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol";

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721Receiver.sol";

contract Tat is IERC721Receiver {
    
constructor() public {}


function approveAndTransfer(IERC721 nft, uint256 _tokenId, address _from, address _to) public {
    require(nft.ownerOf(_tokenId) == msg.sender, "NOT OWNER ");
    nft.isApprovedForAll(_from, address(this));
    nft.safeTransferFrom(msg.sender, _to,_tokenId);
}

function ownerOf(IERC721 nft, uint256 tokenId) external view returns (address owner) {
    return nft.ownerOf(tokenId);
}


function test() public view returns(address sc,address own){
    return (address(this),msg.sender);
}


function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes memory _data) public override returns(bytes4) {
       return 0x150b7a02;
 }

}

Thanks in advance.

1 Like

Hi @Seth_x welcome to the Open Zeppelin Forums!

I think you should take a look at the example https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol just to understand what would normally be in an opposing ERC721 contract that you are calling.

Notice that it is inheriting functions, and then overriding them from the interfaces.

I think you probably have done that already, but what about the opposing ERC721 contract? Does it have the functions to support what you are trying to do?

When you call isApprovedForAll, what is the result? Does it return true or false?

Then move to safeTransferFrom, make sure the _transfer is standard and doesn’t have anything different going on in it.

After taking a look, and you still can’t figure it out, please post the ERC721 contract code and I can take a look at what’s going on in their functions.

@ Tsushima_Yoshiko still having issue, share both erc721 and my SC.
ERC 721 contract.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";

contract MyToken is ERC721, ERC721URIStorage, Ownable {
constructor() ERC721("MyToken", "MTK") {}

function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
    super._burn(tokenId);
}

function tokenURI(uint256 tokenId)
    public
    view
    override(ERC721, ERC721URIStorage)
    returns (string memory)
{
    return super.tokenURI(tokenId);
}

function mintNFT(string memory hash, uint tokenId) public payable {
     _mint(msg.sender,tokenId); //Mint NFT of tweet data 
     _setTokenURI(tokenId,hash);
}

}

and myStore Contract, I have commented whats working and not working.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721Receiver.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";

contract Store is IERC721Receiver {
constructor() public {}

//Working fine
function approveToEthAddress(address nft, address _to) public {
    IERC721 nftAddress = IERC721(nft);
    nftAddress.setApprovalForAll(_to, true); 
}

// Not Working, throws error "ERC721: approve to caller"
function approveToThisContractAddress(address nft, address _to) public {
    IERC721 nftAddress = IERC721(nft);
    nftAddress.setApprovalForAll(address(this), true); 
}

// Not Working,throws error  ERC721: transfer caller is not owner nor approved
function transferIERC721(address nft, uint256 _tokenId, address _from, address _to) public {
    IERC721 nftAddress = IERC721(nft);
    nftAddress.safeTransferFrom(_from, _to,_tokenId);
}

function ownerOf(IERC721 nft, uint256 tokenId) external view returns (address owner) {
    return nft.ownerOf(tokenId);
}

function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes memory _data) public override returns(bytes4) {
       return 0x150b7a02;
 }

}

Hi, I'm having the same problem, I'm calling from another contract running the safeTransferFrom and I'm getting the same error message....
Did you solve it with the approve function?
In my specific case, I'm doing the safeTransferFrom with the admin of the contract, I don't understand why I get the error message.

Best regards

When you are using safeTransferFrom you need to make sure that current owner details should be fetch from network using ownerOf(_tokenId)
Try changing params of SafeTransferFrom like this:
nftAddress.safeTransferFrom(ownerOf(_tokenId), _to,_tokenId);

I hope this change fix your issue.