ORIGINAL POST (EDITED POST BELOW):
I keep getting the following error in my ERC721
Contract:
“ERC721: transfer caller is not owner nor approved”
This happens when I’m trying to buy a Token that has already been minted and transferred by this same contract to another address.
Luckily I’ve discovered that this error message actually comes directly from the require
statement embedded in the standard ERC721
Contract’s transferFrom
function, as seen here:
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);
}
So obviously there’s some kind of permissions/approval business that I need to take care of in order to fix this problem.
The ERC721
Contract provides us with both the approve()
and setApprovalForAll()
functions - both of which facilitate approvals to either specific or ALL addresses (I think), but after reading the docs. I’m actually more confused than ever on how and where exactly I'm supposed to use these functions.
Is it upon the OWNER of a particular Token to call approve()
on their own Token? And if so, when exactly should they be calling that? When they’re minting the Token?
Or is it upon the CONTRACT itself to call setApprovalForAll
- passing in it’s own address during that call, so that it can then be able to facilitate the transfer of Tokens to the various addresses that interact with it?
Just who is supposed to be doing the approving - and when?
======================================
PART 2 - UPDATE:
I should have made this update earlier because I discovered the true source of the problem: it's actually in the first require
statement written in the ERC721
contract's _transfer()
function - which goes like this:
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own!");
require(to != address(0), "ERC721: transfer to the zero address");
...
What's problematic about that first require()
statement is that even if you successfully call approve()
or setApprovalForAll
(and I did so by calling the latter, passing my own contract's address into it, meaning IT was made the operator
by and for every address that owned any Tokens created by it,) well any attempts you'd later make to actually transfer()
any of these tokens would immediately fail as they'd be scuttled by that require()
statement.
And that's because it explicitly requires the transferrer to be the owner of the Token in question. As-in, merely being the operator
is apparently not good enough. At least not the way the contract is currently written by Open-Zeppelin
.
Which of course is quite puzzling.
I mean what good does it do us to make another address be an operator
if at the end of the day this role does not allow it to actually do any transferring of tokens?
I suppose it's a security issue?
Can't quite tell - at least not yet (and please chime in if you have any insights into this.)
I ended up solving this issue by tweaking that require
statement so that it suits my specific needs.
Whether or not doing so compromises the security of my contract in any manner remains to be seen, but I saw/see no other way around it.
Ultimately my original question - or at least my reason for asking it, which is an inquiry into the contract's failure to let us name an operator
- either via approve()
or setApprovalForAll()
- and then have that operator
actually and successfully be able to transact tokens on our behalf, remains.
The way that pesky require
statement is written out of the box scuttles and therefore completely contradicts the very notion of having operators
, and that just doesn't make any sense to me.
If anyone can shed some light on this it'd be great!