Solidity – How to Interact with Any ERC721 Contract Using Remix

erc-721remixsolidity

We can interact with a contract that we have a sample of. To make it clear, please see how Proxy contract interact with Name contract in this response.

In my scenario, I want to check if the msg.sender is the owner of an NFT. So, I'm trying to interact with any ERC721 contract using the same way instead direct contract call. Like this:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/[email protected]/token/ERC721/ERC721.sol";
import "@openzeppelin/[email protected]/token/ERC721/IERC721.sol";

contract Market {
    function putOnSale(address _nftContract, uint256 _tokenId, uint256 _price) public payable {
        address tokenOwner = ERC721(_nftContract).ownerOf(_tokenId); // ???
        require(tokenOwner == msg.sender, 'Only owner...');
        
        // Rest of the code...
    }
}

However, I'm getting this error when I call putOnSale method:

transact to Market.putOnSale errored: VM error: revert.

revert 
    The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance. Debug the transaction to get more information.

Can't I interact with another ERC721 contract using this way?

Note: I'm doing this all in Remix IDE JavaScript VM.

Thanks in advance!

Best Answer

In what little you've provided, the only thing I can see is that you're using the contract where you should be using the interface. i.e. This line:

   address tokenOwner = ERC721(_nftContract).ownerOf(_tokenId); // ???

should be:

    address tokenOwner = IERC721(_nftContract).ownerOf(_tokenId)

If you're not coming from a language where you're used to working with pointers, the difference between these two likely is pretty confusing. In short, you use:

  • The interface when you want to call out to an already deployed contract (IERC721).
  • The contract when you want to deploy a new contract (ERC721).

What you have written now reads like you're trying to deploy a new ERC721 contract, with _nftContract as the name, and then immediately ask for the owner of the given token. The problem here is that according to OpenZepplin's ERC721 API docs, this method requires that the tokenId exists. Since you haven't minted any tokens at this point, this is going to revert the transaction.

If you're still new to Solidity and aren't entirely sure of these things, I recommend avoiding any initial temptation to reduce code down to one liners. You'll find that sometimes VM error: revert is all the more insight that you'll get from the EVM error message. If you have multiple function calls on line, this can be harder to think through.

Writing this as:

    IERC721 nft = IERC721(_nftContract);
    address tokenOwner = nft.ownerOf(_tokenId);

or when deploying new contracts, like this:

    ERC721 nft = new ERC721(_nftName, _nftSymbol);
    nft.mint(msg.sender, _tokenId);

can make it easier to know exactly where things are going wrong, especially since you might not know if the address coming through actually is an ERC721.

Related Topic