ERC721 NFT – Why Owner is Not Approved in ERC721?

erc-721nftsolidity

i writte smart contract – auction of nft. You can see code here: https://pastebin.com/rTQHcLHh

So, i have Users contract. There i have connectUser method, it add new User by msg.sender in mapping. Also here is nftInstance and auctionInstanceUser contract has access to this.

There are collectItem in User contract – create nft FOR USERADDRES (not user contract address) in nftInstance and save it in mapping, sellItem – create auction in auctionInstance, and placeBid

There is problem. There is finalizeAuction method. It transfers nft to highestBidder address. But i get error – ERC721: transfer caller is not owner nor approved. So, in remix i call ownerOf this nft – it's userAddres (it is saved in User contract on init, see code). But if i call method isApproveForAll, i understand that owner is not the user, but a user contract. Try it yourself!

How to fix that?? I can't understand it;(

Best Answer

The code smell on your code is very strong. I looked around and it is a lot of back and forth trying to understand. You are overcomplicating everything. I think you come from a Java world (me too).

For instance, this function does not improve the code:

function itemApproving(address approved) public {
    setApprovalForAll(approved, true);
}

Just use setApprovalForAll(approved, true); everywhere instead of itemApproving(approved);.

Talking about this setApprovalForAll. It has to be called by the owner of the NFT to do what you expect it to do. It cannot be called by random addresses. However, inside Auction.finalizeAuction you call it, so in effect it is the Auction instance that is asking to setApprovalForAll. Ok, it is not forbidden to do.

But! Later the same Auction instance does nftInstance.itemTransfer(owner,.... This is where it does not work, the auction instance is not allowed to effect the transfer.

You got the meaning of setApprovalForAll in reverse. It is the owner who has to call setApprovalForAll(auction.address, true) in a transaction of its own, sometime before the auction resolves.


For the same reason in User.sellItem, when you do:

nftInstance.itemApproving(
    address(auctionInstance.getAuctionById(auctionId))
)

It is not the real owner that did setApproval, but the User instance.


Further simplify your contracts. A sample:

  1. You don't need UserData.exists. It can be replaced by address(UserData.User) != address(0).
  2. In effect, you can reduce your mapping(address => UserData) to mapping(address => User).
  3. Do you really need a User contract?
  4. Doing an unbounded loop like for (uint256 i = 0; i < userTokenIdsCount is a big no-no. Get a better data structure instead.
  5. All your string operations can probably be sent off-chain.
  6. getRandomNumber() is not really random. It just opens new issues or attack vectors when a user calls it twice in a block, or when a sophisticated attacker knows what value it will have.
Related Topic