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)
}
})
}
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 the
buyNFT`
address owner=ERC721.ownerOf(tokenId);
transferFrom(owner, msg.sender,tokenId);