ethereumsoliditysmartcontractsnfterc721

Unable to set approval nor transfer ownership in ERC721 smart contract


Can someone explain to me how I can set approval for ERC721 contract ?

1- At first, the token once minted belongs to the marketplace 2- with buy, I need to transfer ownership of the token to the caller

I keep getting these errors

X Fail with error 'ERC721: approve caller is not token owner nor approved for all' x Fail with error 'ERC721: approve to caller'

This is my smart contract : the mint buy function //The first time a token is created, it is listed here function createToken(string memory tokenURI, string memory name, uint256 price) public payable returns (uint) { uint256 currentTokenId = _tokenIds.current();

    //Mint the NFT with tokenId newTokenId to the address who called createToken
    _safeMint(msg.sender, currentTokenId);

    //Map the tokenId to the tokenURI (which is an IPFS URL with the NFT metadata)
    _setTokenURI(currentTokenId, tokenURI);

    //Helper function to update Global variables and emit an event
    _create(currentTokenId, tokenURI, name, listPrice);

    setApprovalForAll(address(this), true);

    //Increment the tokenId counter, which is keeping track of the number of minted NFTs
    _tokenIds.increment();
    uint256 newTokenId = _tokenIds.current(); 
    return newTokenId;
}

function _create(uint256 tokenId, string memory tokenURI, string memory name,  uint256 price) private {
    //Make sure the sender sent enough ETH to pay for listing
    require(msg.value == listPrice, "Hopefully sending the correct price");
    
    //Just sanity check
    require(price > 0, "Make sure the price isn't negative");

    //Update the mapping of tokenId's to Token details, useful for retrieval functions
    idToToken[tokenId] = Token(
        tokenId,
        tokenURI,
        name,
        payable(address(this)),
        price,
        true
    );

    _transfer(msg.sender, address(this), tokenId);
    //Emit the event for successful transfer. The frontend parses this message and updates the end user
    emit TokenListedSuccess(
        tokenId,
        address(this),
        price,
        true
    );
}
function buyNFT(uint256 tokenId) public payable {
        require(msg.value > 0, "You need to send some ether");
        require(msg.value == idToToken[tokenId].price, "Please submit the asking price in order to complete the purchase");

        approve(msg.sender, tokenId);
        setApprovalForAll(msg.sender, true);

        transferFrom(address(this), msg.sender,tokenId);
       //  safeTransferFrom(address(this), msg.sender, tokenId);
        payable(idToToken[tokenId].owner).transfer(msg.value);

        //update the details of the token
        idToToken[tokenId].owner = payable(msg.sender);
        idToToken[tokenId].isForSale = false;
        _itemsSold.increment();
   
        //Transfer the proceeds from the sale to the seller of the NFT
        payable(address(this)).transfer(msg.value);

        // emit Event
    }

this is my client app :

const buyToken = (...args) => {
            const [tokenId] = args
            return new Promise(async resolve => {
               try {
                    let transaction = await contract.buyNFT(tokenId, { 
                        gasLimit: 5500000, 
                        value: price.toString()
                    });
                    await transaction.wait();
                    resolve()
               } catch (e) {
                  console.error(e)
               }
            })
        }

Solution

  • I think the issue is with

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

    You are transferring from the contract address to the msg.sender but it should be from the nft owner or token id owner.

    The approve() function grants a third party the ability to transfer a single token id. setApprovalForAll() will grant a third party the ability to transfer all of the tokens you own for a given collection.

    When we mint the token we call setApprovalForAll() to authorize the marketplace to transfer our token. This is the trasferFrom

    function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
    
            _transfer(from, to, tokenId);
        }
    

    Before transferring it is checking if the msg.sender is already authorized for this transaction or from address is the "owner" of token. To solve the issue get the owner of the tokenId. Since you are passing the "tokenIdto thebuyNFT`

      address owner=ERC721.ownerOf(tokenId);
      transferFrom(owner, msg.sender,tokenId);