Skip to content

Pseudo-Introspection, or standard interface detection #165

Closed
@chriseth

Description

This EIP is being further developed as #881


For some "standard interfaces" like the token interface ( #20 ), it is sometimes useful to query whether a contract supports the interface and if yes, which version of the interface, in order to adapt the way in which the contract is interfaced with. Specifically for #20, a version identifier has already been proposed. This proposal wants to generalize the concept of interfaces and interface versions to interface identifiers:

Every "standard interface" should be assigned a unique identifier (the sha3 hash of its source representation would be suitable) as a bytes32 value. Interfaces that support pseudo-introspection should provide the following methods. Note that this is especially targeted at contracts that can also provide multiple non-conflicting interfaces. This is useful if e.g. a standard token contract also allows a "cheque" functionality, which would have its own interface ID, or if the token does not support allowances.

/// @returns true iff the interface is supported
function supportsInterface(bytes32 interfaceID) constant returns (bool);
/// @returns the (main) interface ID of this contract
function interfaceID() constant returns (bytes32);
/// @returns a bit mask of the supported interfaces.
function supportsInterfaces(bytes32[] interfaceIDs) constant returns (bytes32 r) {
  if (interfaceIDs.length > 256) throw;
  for (uint i = 0; i < interfaceIDs.length; i++)
    if (supportsInterface(interfaceIDs[i]))
      r |= bytes32(2**i);
}

Example implementation and usage:

contract GenericInterfaceContract {
    function interfaceID() constant returns (bytes32);
    function supportsInterface(bytes32 _interfaceID) constant returns (bool);
    function supportsInterfaces(bytes32[] interfaceIDs) constant returns (bytes32 r) {
      if (interfaceIDs.length > 256) throw;
      for (uint i = 0; i < interfaceIDs.length; i++)
        if (supportsInterface(interfaceIDs[i]))
          r |= bytes32(2**i);
    }
}
contract SimpleToken is GenericInterfaceContract {
    bytes32 public constant interfaceID = 0xf2a53462c853462ca86b71b97dd84af6a2f7689fc12ea917d9029117d32b9fde;
    event Transfer(address indexed _from, address indexed _to, uint256 _value);

    function totalSupply() constant returns (uint256 supply);
    function balanceOf(address _owner) constant returns (uint256 balance);
    function transfer(address _to, uint256 _value) returns (bool success);

    function supportsInterface(bytes32 _interfaceID) constant returns (bool) {
        return _interfaceID == interfaceID;
    }
}
contract Token is SimpleToken {
    bytes32 public constant interfaceID = 0xa2f7689fc12ea917d9029117d32b9fdef2a53462c853462ca86b71b97dd84af6;
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
    function approve(address _spender, uint256 _value) returns (bool success);
    function allowance(address _owner, address _spender) constant returns (uint256 remaining);

    function supportsInterface(bytes32 _interfaceID) constant returns (bool) {
        return _interfaceID == interfaceID || super.supportsInterface(_interfaceID);
    }
}
contract C {
    function retrieveRemainingAllowance(GenericInterfaceContract c, address owner) returns (uint) {
        if (c.supportsInterface(Token.interfaceID)) {
            Token t = Token(c);
            return Token(c).allowance(owner, msg.sender);
        } else if (c.supportsInterface(SimpleToken.interfaceID)) {
            return 0;
        } else {
            throw;
        }
    }
}

Note:
Since the EVM does not provide any guarantees for the semantics of contracts, any such information returned by other contracts can only be used as a guideline.

Note2:
The convenient access to the interface ID via Token.interfaceID is not yet supported by solidity ( ethereum/solidity#1290 ).

Credits: This is the result of a discussion with @konradkonrad and @frozeman

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions