EIP.tools

EIP.tools

๐ŸŽ‰ FinalStandards Track: ERC

ERC-165: Standard Interface Detection

Authors
Created2018-01-23
Requires
Referenced by

ERC-634

ERC-721

ERC-725

ERC-820

ERC-926

ERC-998

ERC-1155

ERC-1261

ERC-1337

ERC-1363

ERC-1417

ERC-1450

ERC-1616

ERC-1633

ERC-1656

ERC-1705

ERC-1761

ERC-1820

ERC-1833

ERC-1844

ERC-1907

ERC-1922

ERC-1923

ERC-2004

ERC-2135

ERC-2390

ERC-2477

ERC-2615

ERC-2767

ERC-2981

ERC-3386

ERC-3525

ERC-4353

ERC-4393

ERC-4400

ERC-4494

ERC-4519

ERC-4524

ERC-4671

ERC-4675

ERC-4799

ERC-4885

ERC-4906

ERC-4907

ERC-4910

ERC-4973

ERC-4974

ERC-4987

ERC-5005

ERC-5006

ERC-5007

ERC-5008

ERC-5023

ERC-5050

ERC-5058

ERC-5169

ERC-5173

ERC-5187

ERC-5192

ERC-5216

ERC-5289

ERC-5313

ERC-5334

ERC-5380

ERC-5409

ERC-5437

ERC-5453

ERC-5484

ERC-5489

ERC-5501

ERC-5507

ERC-5516

ERC-5521

ERC-5560

ERC-5604

ERC-5633

ERC-5635

ERC-5646

ERC-5679

ERC-5700

ERC-5727

ERC-5732

ERC-5750

ERC-5753

ERC-5773

ERC-5827

ERC-5982

ERC-6059

ERC-6066

ERC-6105

ERC-6120

ERC-6147

ERC-6150

ERC-6220

ERC-6239

ERC-6268

ERC-6315

ERC-6381

ERC-6454

ERC-6464

ERC-6551

ERC-6604

ERC-6672

ERC-6785

ERC-6786

ERC-6884

ERC-6900

ERC-6909

ERC-6956

ERC-6982

ERC-7007

ERC-7066

ERC-7092

ERC-7160

ERC-7204

ERC-7208

ERC-7231

ERC-7291

ERC-7303

ERC-7401

ERC-7409

ERC-7410

ERC-7417

ERC-7432

ERC-7439

ERC-7444

ERC-7496

ERC-7498

ERC-7508

ERC-7513

ERC-7518

ERC-7527

ERC-7531

ERC-7540

ERC-7546

ERC-7548

ERC-7564

ERC-7574

ERC-7575

ERC-7578

ERC-7579

ERC-7585

ERC-7586

ERC-7589

ERC-7590

ERC-7603

ERC-7604

ERC-7621

ERC-7629

ERC-7632

ERC-7634

ERC-7651

ERC-7652

ERC-7653

ERC-7656

ERC-7661

ERC-7695

ERC-7699

ERC-7714

ERC-7721

ERC-7753

ERC-7755

ERC-7757

ERC-7765

ERC-7802

ERC-7829

ERC-7831

ERC-7858

ERC-7860

ERC-7861

ERC-7866

ERC-7878

ERC-7887

ERC-7893

ERC-7897

EIP-7899

ERC-7909

ERC-7943

ERC-7970

ERC-7978

ERC-7984

ERC-7992

ERC-7996

ERC-8027

ERC-8034

ERC-8035

ERC-8036

ERC-8047

ERC-8048

ERC-8049

ERC-8056

ERC-8060

ERC-8063

ERC-8064

ERC-8065

ERC-8067

ERC-8083

ERC-8097

ERC-8107

ERC-8112

ERC-8114

ERC-8118

ERC-8129

ERC-8143

ERC-8161

ERC-8165

ERC-8169

ERC-8171

ERC-8174

ERC-8181

ERC-8186

ERC-8191

ERC-8192

ERC-8194

ERC-8195

ERC-8199

ERC-8203

ERC-8214

ERC-8216

ERC-8220

ERC-8221

ERC-8226

ERC-8227

ERC-8228

ERC-8229

ERC-8231

ERC-8234

ERC-8236

ERC-8238

ERC-8240

ERC-8241

ERC-8257

ERC-8258

ERC-8259

ERC-8260

ERC-8262

ERC-8264

ERC-8267

ERC-8273

ERC-8274

ERC-8276

ERC-8278

ERC-8280

ERC-8281

ERC-8284

ERC-8285

ERC-8305

ERC-8306

ERC-8307

ERC-8308

ERC-8312

ERC-8318

ERC-8320

ERC-8323

ERC-8325

ERC-8326

ERC-8327

ERC-8328

ERC-8329

ERC-8330

ERC-8332

ERC-8335

ERC-8338

ERC-8339

ERC-8343

ERC-8344

ERC-8348

ERC-8349

ERC-8354

ERC-8356

ERC-8370

ERC-8376

ERC-8377

ERC-8378

ERC-8382

ERC-8391

ERC-8393

ERC-8405

ERC-8407

ERC-8412

ERC-8414

ERC-8415

ERC-8416

ERC-8417

ERC-8418

ERC-8419

ERC-8423

ERC-8424

ERC-8426

ERC-8428

ERC-8431

ERC-8432

ERC-8434

ERC-8436

Markdown

https://raw.githubusercontent.com/ethereum/ERCs/ma...

EIP-GPT summary

Contents
Simple SummaryAbstractMotivationSpecificationHow Interfaces are IdentifiedHow a Contract will Publish the Interfaces it ImplementsHow to Detect if a Contract Implements ERC-165How to Detect if a Contract Implements any Given InterfaceRationaleBackwards CompatibilityTest CasesImplementationVersion historyCopyright

Simple Summary

Creates a standard method to publish and detect what interfaces a smart contract implements.

Abstract

Herein, we standardize the following:

  1. How interfaces are identified
  2. How a contract will publish the interfaces it implements
  3. How to detect if a contract implements ERC-165
  4. How to detect if a contract implements any given interface

Motivation

For some "standard interfaces" like the ERC-20 token interface, 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 to be interacted with. Specifically for ERC-20, a version identifier has already been proposed. This proposal standardizes the concept of interfaces and standardizes the identification (naming) of interfaces.

Specification

How Interfaces are Identified

For this standard, an interface is a set of function selectors as defined by the Ethereum ABI. This a subset of Solidity's concept of interfaces and the interface keyword definition which also defines return types, mutability and events.

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

pragma solidity ^0.4.20;

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;
    }
}

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

How a Contract will Publish the Interfaces it Implements

A contract that is compliant with ERC-165 shall implement the following interface (referred as ERC165.sol):

pragma solidity ^0.4.20;

interface ERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. 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);
}

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

Therefore the implementing contract will have a supportsInterface function that returns:

  • true when interfaceID is 0x01ffc9a7 (EIP165 interface)
  • false when interfaceID is 0xffffffff
  • true for any other 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 Detect if a Contract Implements ERC-165

  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 ERC-165.
  3. If the call returns true, a second call is made with input data 0x01ffc9a7ffffffff00000000000000000000000000000000000000000000000000000000.
  4. If the second call fails or returns true, the destination contract does not implement ERC-165.
  5. Otherwise it implements ERC-165.

How to Detect if a Contract Implements any Given Interface

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

Rationale

We tried to keep this specification as simple as possible. This implementation is also compatible with the current Solidity version.

Backwards Compatibility

The mechanism described above (with 0xffffffff) should work with most of the contracts previous to this standard to determine that they do not implement ERC-165.

Also the ENS already implements this EIP.

Test Cases

Following is a contract that detects which interfaces other contracts implement. From @fulldecent and @jbaylina.

pragma solidity ^0.4.20;

contract ERC165Query {
    bytes4 constant InvalidID = 0xffffffff;
    bytes4 constant ERC165ID = 0x01ffc9a7;

    function doesContractImplementInterface(address _contract, bytes4 _interfaceId) external view returns (bool) {
        uint256 success;
        uint256 result;

        (success, result) = noThrowCall(_contract, ERC165ID);
        if ((success==0)||(result==0)) {
            return false;
        }

        (success, result) = noThrowCall(_contract, InvalidID);
        if ((success==0)||(result!=0)) {
            return false;
        }

        (success, result) = noThrowCall(_contract, _interfaceId);
        if ((success==1)&&(result==1)) {
            return true;
        }
        return false;
    }

    function noThrowCall(address _contract, bytes4 _interfaceId) constant internal returns (uint256 success, uint256 result) {
        bytes4 erc165ID = ERC165ID;

        assembly {
                let x := mload(0x40)               // Find empty storage location using "free memory pointer"
                mstore(x, erc165ID)                // Place signature at beginning of empty storage
                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature

                success := staticcall(
                                    30000,         // 30k gas
                                    _contract,     // To addr
                                    x,             // Inputs are stored at location x
                                    0x24,          // Inputs are 36 bytes long
                                    x,             // Store output over input (saves space)
                                    0x20)          // Outputs are 32 bytes long

                result := mload(x)                 // Load the result
        }
    }
}

Implementation

This approach uses a view function implementation of supportsInterface. The execution cost is 586 gas for any input. But contract initialization requires storing each interface (SSTORE is 20,000 gas). The ERC165MappingImplementation contract is generic and reusable.

pragma solidity ^0.4.20;

import "./ERC165.sol";

contract ERC165MappingImplementation is ERC165 {
    /// @dev You must not set element 0xffffffff to true
    mapping(bytes4 => bool) internal supportedInterfaces;

    function ERC165MappingImplementation() internal {
        supportedInterfaces[this.supportsInterface.selector] = true;
    }

    function supportsInterface(bytes4 interfaceID) external view returns (bool) {
        return supportedInterfaces[interfaceID];
    }
}

interface Simpson {
    function is2D() external returns (bool);
    function skinColor() external returns (string);
}

contract Lisa is ERC165MappingImplementation, Simpson {
    function Lisa() public {
        supportedInterfaces[this.is2D.selector ^ this.skinColor.selector] = true;
    }

    function is2D() external returns (bool){}
    function skinColor() external returns (string){}
}

Following is a pure function implementation of supportsInterface. The worst-case execution cost is 236 gas, but increases linearly with a higher number of supported interfaces.

pragma solidity ^0.4.20;

import "./ERC165.sol";

interface Simpson {
    function is2D() external returns (bool);
    function skinColor() external returns (string);
}

contract Homer is ERC165, Simpson {
    function supportsInterface(bytes4 interfaceID) external view returns (bool) {
        return
          interfaceID == this.supportsInterface.selector || // ERC165
          interfaceID == this.is2D.selector
                         ^ this.skinColor.selector; // Simpson
    }

    function is2D() external returns (bool){}
    function skinColor() external returns (string){}
}

With three or more supported interfaces (including ERC165 itself as a required supported interface), the mapping approach (in every case) costs less gas than the pure approach (at worst case).

Version history

  • PR 1640, finalized 2019-01-23 -- This corrects the noThrowCall test case to use 36 bytes rather than the previous 32 bytes. The previous code was an error that still silently worked in Solidity 0.4.x but which was broken by new behavior introduced in Solidity 0.5.0. This change was discussed at #1640.

  • EIP 165, finalized 2018-04-20 -- Original published version.

Copyright and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth