2

I am building an NFT marketplace where the user actions are as follows.

  • Address A uploads an image to create an NFT. This action calls smart contract functions createToken() to create ERC721 token and listNFTItem() to list the token.
  • Now, if Address B wants to buy this token, this action is executed by calling Buy() that in turn calls a transferFrom() within it.
  • Now ideally ownership of the token should be transferred to Address B from Address A and Address B should be able relist it.
  • Here is where I am facing the problem. When Address B is relisting this token using relistNftItem() that calls transferFrom(), this action fails with an error 'ERC721: transfer caller is not owner nor approved'

Now after researching for a bit in stackoverflow and reading openzeppelin contracts, I realise that this error is a fairly common and 'Address B' just needs to approve the 'to' address(which is a contract address in this case) before relisting. But adding an approve(address(this), tokenId) in relistNftItem() function raises this new error 'ERC721: approve caller is not owner nor approved for all'. The error is same even when I replace address(this) with msg.sender in approve().

Here is the createToken()

function createToken(string memory tokenURI) public returns (uint256) {
    // Token URI contains metadata like msg.sender etc
    _tokenIds.increment();
    uint256 newItemId = _tokenIds.current();

    _mint(msg.sender, newItemId);
    _setTokenURI(newItemId, tokenURI);

    // Giving approval to the contract address for transferring ownership from all nfts

    setApprovalForAll(contractAddress, true);

    return newItemId;
}

Here is my listNFTItem() function on smart contract

function listNFTItem(
    address nftContract,
    uint256 tokenId,
    uint256 price
) public payable nonReentrant {
    require(price > 0, "Price must be at least 1 wei");
    require(
        msg.value == listingPrice,
        "Price must be equal to listing price"
    );

    _itemIds.increment();
    uint256 itemId = _itemIds.current();

    idToMarketItem[itemId] = MarketItem(
        itemId,
        nftContract,
        tokenId,
        payable(msg.sender),
        payable(address(0)),
        price,
        false
    );
    IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);

    emit MarketItemCreated(
        itemId,
        nftContract,
        tokenId,
        msg.sender,
        address(0),
        price,
        false
    );
}

Here is the Buy()

function Buy(address nftContract, uint256 itemId)
    public
    payable
    nonReentrant
{
    uint256 price = idToMarketItem[itemId].price;
    uint256 tokenId = idToMarketItem[itemId].tokenId;
    require(
        msg.value == price,
        "Please submit the asking price in order to complete the purchase"
    );

    idToMarketItem[itemId].seller.transfer(msg.value);

    IERC721(nftContract).transferFrom(address(this), msg.sender, tokenId);

    idToMarketItem[itemId].owner = payable(msg.sender);
    idToMarketItem[itemId].sold = true;
    _itemsSold.increment();
    payable(owner).transfer(listingPrice);
}

Here is the contract code for relistNFTitem()

function relistNFTitem(address nftContract, uint256 itemId)
    public
    payable
    nonReentrant
{
    uint256 price = idToMarketItem[itemId].price;
    uint256 tokenId = idToMarketItem[itemId].tokenId;
    require(
        msg.value == price,
        "Please submit the asking price in order to complete the purchase"
    );
    require(
        idToMarketItem[itemId].owner == msg.sender,
        "Only token owner can put up token for sell"
    );

    idToMarketItem[itemId].seller.transfer(msg.value);

    // I am approving the approve contract here. Commented it out to reproduce step by step. 

    // IERC721(nftContract).approve(address(this), tokenId);

    IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId );
    idToMarketItem[itemId].owner = payable(address(this));
    idToMarketItem[itemId].sold = false;
    payable(owner).transfer(listingPrice);
}

I am assuming that buy() transfers ownership to 'Address B' in the buy(). If not, How to grant approval for address 'B' to relist. If my line of thought is wrong, let me know what am I missing. Please guide me on how to relist the token/ freely tradable.

1
  • can you explique more how to resolve this issue 🙏 🙏 Commented Feb 27, 2022 at 17:33

1 Answer 1

1

I'm running into the same exact issue. Did you ever solve this?

[SOLVED]

I had to create a second transaction to the NFT ERC721 contract that allowed the NFT Marketplace Contract to be approved to list any of that addresses tokens.

async function allowListing() {
  // connect to the web3 modal and get the signer
  const web3Modal = new Web3Modal();
  const connection = await web3Modal.connect();
  const provider = new ethers.providers.Web3Provider(connection);
  const signer = provider.getSigner();

  // allow token from the NFT contract to be listed on the markeplace
  let contract = new ethers.Contract(nftAddress, NFT.abi, signer);
  let transaction = await contract.setApprovalForAll(nftMarketAddress, true);
  await transaction.wait();
}
1
  • Thank You Mr. Same Issue. If I may ask, how did you figure this out? Commented Jan 30, 2022 at 6:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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