ERC-165 Query – Understanding ERC-165 Query on ERC-721 Implementation

erc-721tokens

While testing ERC-721 implementation from https://github.com/m0t0k1ch1/ERC721-token-sample/blob/master/contracts/MyNonFungibleToken.sol and trying to query the method supportsInterface I got the expected result in this case:

// bytes4(keccak256('supportsInterface(bytes4)')) = 01ffc9a7
supportsInterface(0x01ffc9a7);
    bool: true

But not in this other case:

// Checking for example support for function tokensOfOwner()
supportsInterface(0x8462151c);
    bool: false

Any idea why returns false on second case? If worng, how should I properly query the function? Thx!

Best Answer

supportsInterface shouldn't be used to check individual functions. Each interface should have a single interfaceID which, as you can see in the ERC-721 implementation you posted, is constructed as follows:

bytes4 constant InterfaceID_someInterface =
    bytes4(keccak256('function1(address)')) ^
    bytes4(keccak256('function2(uint,etc)')) ^
    ...
    bytes4(keccak256('functionN()'));

In the case of the ERC165 interface, there is only one function in that interface, supportsInterface, so the interfaceID is just

bytes4 constant InterfaceID_ERC165 =
    bytes4(keccak256('supportsInterface(bytes4)'));
//0x01ffc9a7

But it's important to note that when you call supportsInterface on 0x01ffc9a7, you are testing to see if your contract supports the whole ERC165 standard. It just so happens that there is only one function.

So you shouldn't be calling supportsInterface on 0x8462151c, this would be checking for an interface which only contained the function tokensOfOwner().

Instead, you should be checking against the interfaceID of the whole ERC721 interface. If your contract supports the whole interface, then by definition it will support any of the functions contained therein.

The idea is that anyone can find the specification for the interface, and just check the interfaceID of that interface against your contract.

BUT, there are two problems with the implementation posted. First of all, they didn't provide the ERC721 or 165 interfaces. They just provided an abstract contract which contains a few related functions. So they didn't really give the thing you should be checking against.

Also, I don't think this implementation is an up-to-date or proper implementation of ERC721. The standard is still a work in progress, but you can see the non-finalised standard here.

There are actually interfaces for 3 standards here:

ERC721 (0x80ac58cd)

ERC721Metadata (0x5b5e139f) (optional)

ERC721Enumerable (0x780e9d63) (optional)

ERC721 just implements the basic requirements of a NFT, ERC721Metadata adds name, symbol and URI data functionality, and ERC721Enumerable adds some other functionality to do with token indexes.

This is relevant to what you posted because basic ERC721 does not implement any of the following:

name() (this is from ERC721Metadata)

symbol() (this is from ERC721Metadata)

totalSupply() (this is from ERC721Enumerable)

transfer(address,uint256) (this is from ERC20 spec, 721 uses transferFrom and safeTransferFrom)

tokensOfOwner(address) (not part of the standards).

There are also some functions in the standard which you will see aren't implemented in the one you provided. It's likely that it was written when the standard was at a much earlier age. And indeed, this sort of situation is partially the purpose of the ERC165 specification.

If standards are developing and evolving over time, then ERC721 version 0.0.1 (a number I just made up) may have an interfaceID of 0x9f40b779 (which is the value of InterfaceID_ERC721 in the contract you provided). However, version 0.0.2 may add, change, or remove a function, which would completely change the interfaceID to something else. So as long as you specify that your contract complies to ERC721 0.0.1, someone else can look up the interface, and check it against your contract.

So the very short answer to your question is, you shouldn't be checking supportsInterface against a single function, but rather against the whole interface.

But I also don't think you should be using the contract you provided because it seems out of date, and the whole purpose of standards is to be compliant.

Related Topic