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.

    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!

  • Try to inject into Testnet and test it. I tried on Rinkeby testnet and it works! Mar 21, 2022 at 14:31
  • @Kerry99 did you mean injected Web3? I tried but it didn't work.
    – anileates
    Mar 21, 2022 at 16:46
  • Are you trying to send any Ether when you call putOnSale? If not, that's what flagging your error. You only want to declare a method payable if you intend on directly sending Ether (specifically, not any other ERC-20) to it.
    – Joe Habel
    Mar 22, 2022 at 1:10
  • Yes, I'm sending Ether on call. I tried to remove payable and call without Ether also. @JoeHabel
    – anileates
    Mar 22, 2022 at 6:13

1 Answer 1


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.

  • Thank you for the great explanation and information, @Joe Habel! You saved me. I tested your suggestions and I found the solution. But, the problem is not about using or not using the interface. In both cases, I can interact with the contract I want. But make sure that contract is in the same chain, I mean, it must be in the Rinkeby for example as we're working in Rinkeby. :) I was trying to call a main-net contract, so there is no contract to interact with. I recognized it after I write the code in multiple lines.
    – anileates
    Mar 23, 2022 at 14:29

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.