It seems to me that pre 0.5 NFTs are not compatible with more modern solidity versions. I am trying to use the contract written on ^0.7.6 to call "safeTransferFrom(address,address,uint)" from the ^0.4.x contract, and it's causing reverts, even when I try to do a low level call.

The 0.4.x NFT is already deployed. I am trying to deploy a 0.7.6 contract that can make calls to this contract.

Here is the interface of the 0.4.x contract I'm trying to interface with:

pragma solidity ^0.4.21;

 * @title ERC721 Non-Fungible Token Standard basic interface
 * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
contract ERC721Basic {
  function ownerOf(uint256 _tokenId) public view returns (address _owner);
  function exists(uint256 _tokenId) public view returns (bool _exists);
  function approve(address _to, uint256 _tokenId) public;
  function getApproved(uint256 _tokenId) public view returns (address _operator);
  function setApprovalForAll(address _operator, bool _approved) public;
  function isApprovedForAll(address _owner, address _operator) public view returns (bool);
  function transferFrom(address _from, address _to, uint256 _tokenId) public;
  function safeTransferFrom(address _from, address _to, uint256 _tokenId) public;


Here are the important parts of the function I want to call from my 0.7.6 contract:

    function _executeOrder(
        address nftAddress,
        uint256 assetId,
        uint256 price,
        bytes memory fingerprint
    ) internal returns (Order memory) {
        ERC721BasicInterface nftRegistry = ERC721Interface(nftAddress);

        nftRegistry.safeTransferFrom(order.seller, _msgSender(), assetId);

The issue is, what should ERC721Interface be defined as in my 0.7.6 contract? Clearly, the interface written in 0.4.21 is not compatible with 0.7.6 (external function modifiers). I tried calling safeTransferFrom using a more modern ERC721 interface on the 0.4.21 NFT but the transactions were reverted. This is correct behavior I'm assuming, since the interface is different.

  • Can you add some code from both ends. And answer Lauri's question about deployment. Commented Oct 5, 2021 at 9:12
  • 1
    Done. Please take a look and let me know if anything should be added or changed
    – antioi
    Commented Oct 5, 2021 at 16:06

2 Answers 2


I suspect this has nothing to do with Solidity versions. The safeTransferFrom function in Solidity really means keccak256("safeTransferFrom(address,address,uint256)") for the Ethereum Virtual Machine. But let's not get lost with details.

When you call safeTransferFrom, the caller of this function needs to have been approved via the approve function, like so: approve(futureCaller, tokenId). When this is done, you can confirm who is approved for transfer with getApproved(tokenId).

Now, when you call safeTransferFrom from the _executeOrder in your own 0.7.6 contract, the caller is your 0.7.6 contract. So your contract needs to be pre-approved by the token owner.

So the solution is to first do (example in Javascript here):

await the_0_4_Nft.methods
    .approve(my_0_7_6_contract_address, the_token_id)
    .send({ from: token_owner_address });

You can simply write an interface with the 0.7.6 version. The interface doesn't care with what Solidity version the target contract is compiled with - it simply states what functionality the target contract has. You can basically write the interface in any version you want (although best to write it with a version which explicitly supports interfaces).

I just changed the version number and replaced public with external. So your interface could look like this:

pragma solidity ^0.7.6;

interface ERC721BasicInterface {
  function ownerOf(uint256 _tokenId) external view returns (address _owner);
  function exists(uint256 _tokenId) external view returns (bool _exists);
  function approve(address _to, uint256 _tokenId) external;
  function getApproved(uint256 _tokenId) external view returns (address _operator);
  function setApprovalForAll(address _operator, bool _approved) external;
  function isApprovedForAll(address _owner, address _operator) external view returns (bool);
  function transferFrom(address _from, address _to, uint256 _tokenId) external;
  function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
  • The 0.4.x contract is already deployed. How do I create an interface for 0.4.x on 0.7.6? 0.4.x doesn't even have interfaces right?
    – antioi
    Commented Oct 5, 2021 at 15:28
  • Just the same way you'd create an interface for a 0.7.6 contract. The interface doesn't care about what compiler version was used in the contract which is being used by the interface, it works the same for all compiler versions. Commented Oct 5, 2021 at 15:37
  • Can you take a look at the code I've added? I don't think it's possible to do what you're suggesting
    – antioi
    Commented Oct 5, 2021 at 16:06
  • Edited my answer Commented Oct 5, 2021 at 16:33

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.