Developers Forum for XinFin XDC Network

Cover image for Proposal for Interface Query Standard XIP
Jon McBee
Jon McBee

Posted on

Proposal for Interface Query Standard XIP

This proposed XIP defines a standard to publish and detect what interfaces a smart contract implements. In smart contract development, a standard called EIP-165 often appears when contract to contract interaction is needed. This XIP proposes a standard that duplicates the functionality of EIP-165.

Since there is no clear way to find what functions are implemented in a contract, this XIP proposes a standard method to define and query interfaces in a contract. For example, if we define an interface identifier of XIP-8, we can easily determine a contract implements XIP-8 or not.

If a contract follows this standard, it will publish what interfaces it supports. Then, other contracts can utilize the published information to avoid calling unsupported functions.

How Interface Identifiers are Defined

An interface identifier is a combination of function selectors of a contract.
A function selector is four bytes of keccak256 hash of the signature of a function
(e.g., bytes4(keccak256('supportsInterface(bytes4)'))).
The signature is defined as the canonical expression of the basic prototype without parameter names and the return type.

We define the interface identifier as the XOR of all function selectors in the interface. This code example below shows how to calculate an interface identifier:

pragma solidity ^0.4.24;
interface Solidity101 {
    function hello() external pure;
    function world(int) external pure;
}
contract Selector {
    function calculateSelector() public pure returns (bytes4) {
        Solidity101 i;
        return i.hello.selector ^ i.world.selector;
    }
}
Enter fullscreen mode Exit fullscreen mode

Note: interfaces do not permit optional functions, therefore, the interface identifier will not include them.

How a Contract Publishes the Interfaces it Implements

A contract that is compliant with this XIP shall implement the following interface (referred as InterfaceIdentifier.sol):

pragma solidity ^0.4.24;
interface InterfaceIdentifier {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as defined in this XIP.
    /// @dev Interface identifier is defined in this XIP. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise.
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
Enter fullscreen mode Exit fullscreen mode

The interface identifier for this interface is 0x01ffc9a7.
You can calculate this by running bytes4(keccak256('supportsInterface(bytes4)')); or using the Selector contract above.

The implementing contract will have a supportsInterface function, and it returns:

  • true when interfaceID is 0x01ffc9a7 (supportsInterface itself)
  • false when interfaceID is 0xffffffff
  • true for interfaceID this contract implements
  • false for any other interfaceID

This function must return a bool and use at most 30,000 gas.

Implementation note: there are several logical ways to implement this function. Please see the example implementations and the discussion on gas usage.

How to Query if a Contract Implements This XIP

  1. The source contract makes a STATICCALL to the destination address with input data: 0x01ffc9a701ffc9a700000000000000000000000000000000000000000000000000000000 and gas 30,000. This corresponds to contract.supportsInterface(0x01ffc9a7).
  2. If the call fails or return false, the destination contract does not implement this XIP.
  3. If the call returns true, a second call is made with input data 0x01ffc9a7ffffffff00000000000000000000000000000000000000000000000000000000. This corresponds to contract.supportsInterface(0xffffffff).
  4. If the second call fails or returns true, the destination contract does not implement this XIP.
  5. Otherwise it implements this XIP.

How to Query if a Contract Implements any Given Interface

  1. If you are not sure if the contract implements this XIP, use the above procedure to confirm.
  2. If it does not implement this XIP, then you will have to see what methods it uses in other way.
  3. If it implements this XIP then just call supportsInterface(interfaceID) to determine if it implements an interface you can use.

Now we have a way to calculate the interface ID. However, you might still have a few questions:

Why using a bytes4 value as the interface ID, instead of something like a string or a bytes32 value?
What is a function selector?
Why do we use XOR to calculate the interface ID?

For question 1, a string or bytes32 value could distinguish interfaces, but the problem is they would take way more than 4 bytes to store, which is too costly. Note that an Ethereum full node needs to store all the data, including interface IDs for all interface defined. A standard needs to balance both the cost of storage and the chance of collision. The chance of collision means the chance for two different interfaces having the same interface ID. So bytes4 was chosen with the consideration of such balance.

For question 2, function selector is a bytes4 value that allows you to perform dynamic invocation of a function, based on the name of the function and the type of each one of the input arguments. You can think of it as the identifier of a function. In solidity, we can get a function selector by reading the selector property of a function. For example, we can make a Selector contract as below to print the function selector:

/// Selector.sol
pragma solidity 0.5.8;
import "./StoreInterface.sol";
contract Selector {
  /// 0x20965255
  function getValueSelector() external pure returns (bytes4) {
    StoreInterface i;
    return i.getValue.selector;
  }
}
Enter fullscreen mode Exit fullscreen mode

For question 3, XOR has a nice property that any change to the function defined in the interface (i.e, function name, or arguments type) will result the calculated interface ID to be changed, and meanwhile the length of the total bytes stays unchanged, no matter how many functions are included in the interface.

Please read through the proposed XIP and leave comments here on this post. If the developer community supports it I will shepherd it through the process.

Discussion (11)

Collapse
tpada13 profile image
Jonathan

Jon,

I agree. This is a great next step. EIP-165 brings needed standardization to the network in defining contract interfaces. This is particularly necessary as more NFT market places and projects build on XDC. It is even more important when new contract standards (XRC standard) are proposed / created as it will ensure the correct contract is being used across projects. I'm in favor for moving this forward as an XIP.

Collapse
lance profile image
Lance Lilly

Yes

Collapse
sean_ profile image
Sean

VOTE = YES !

Collapse
james_love_23979d134fec2b profile image
James Love

Vote = Yes

Collapse
ronald_mitchell_0de6c6219 profile image
Ronald Mitchell

Vote: YES

Collapse
ravikumar_j_5016c023f6919 profile image
Ravikumar J

Vote = Yes

Collapse
harleyhermanson profile image
harley hermanson

Yes

Collapse
s4njk4n profile image
s4njk4n

Vote = Yes

Collapse
mogithehurt profile image
mogithehurt

VOTE = YES

Collapse
menezesphill profile image
Phill Menezes

VOTE = Yes!

Collapse
n_w_rct profile image
Nick | RCT

Vote = Yes!