3

I'm not sure I understand how overriding a function works in solidity. I am working on an ERC721 token based on the OpenZeppelin implementation. OpenZeppelin's ERC721.sol contains the following functions:

contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }
}

In my contract, I would like to override ownerOf, so I have the following code:

contract MyNFT is
    Context,
    AccessControlEnumerable,
    Ownable,
    ERC721Enumerable,
    ERC721Pausable
{
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "DramaLlamas: ownership query for nonexistent token");

        address owner = _owners[tokenId];
        if (owner == address(0)) {
            return _creatorAddress;
        } else {
            return owner;
        }
    }
}

When I call ownerOf externally, this works as expected. However, when I call transferFrom, the code is using the original version of ownerOf NOT the overridden version in MyNFT. This is not the behavior I would expect. I want every call for ownerOf to use the overridden function.

Is there any way to achieve what I'm trying to do without creating overrides of all the functions in OpenZeppelin's implementation?

1 Answer 1

1

The blame is on _isApprovedOrOwner it is calling explicitly the implementation on ERC721 due to ERC721.ownerOf.

function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
    require(_exists(tokenId), "ERC721: operator query for nonexistent token");
    address owner = ERC721.ownerOf(tokenId);
    return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}

If you override ownerOf it will still call to the ERC721 implementation.

Perhaps you want to override _isApprovedOrOwner instead.

1
  • Great catch! Unfortunately, the OpenZeppelin ERC721 implementation has several instances where this ownerOf call is hardcoded. I ended up forking it and changing it to my requirements.
    – Bintz
    Commented Oct 22, 2021 at 12:08

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.