ERC165Checker.sol 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. pragma solidity ^0.4.24;
  2. /**
  3. * @title ERC165Checker
  4. * @dev Use `using ERC165Checker for address`; to include this library
  5. * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
  6. */
  7. library ERC165Checker {
  8. // As per the EIP-165 spec, no interface should ever match 0xffffffff
  9. bytes4 private constant InterfaceId_Invalid = 0xffffffff;
  10. bytes4 private constant InterfaceId_ERC165 = 0x01ffc9a7;
  11. /**
  12. * 0x01ffc9a7 ===
  13. * bytes4(keccak256('supportsInterface(bytes4)'))
  14. */
  15. /**
  16. * @notice Query if a contract supports ERC165
  17. * @param _address The address of the contract to query for support of ERC165
  18. * @return true if the contract at _address implements ERC165
  19. */
  20. function supportsERC165(address _address)
  21. internal
  22. view
  23. returns (bool)
  24. {
  25. // Any contract that implements ERC165 must explicitly indicate support of
  26. // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
  27. return supportsERC165Interface(_address, InterfaceId_ERC165) &&
  28. !supportsERC165Interface(_address, InterfaceId_Invalid);
  29. }
  30. /**
  31. * @notice Query if a contract implements an interface, also checks support of ERC165
  32. * @param _address The address of the contract to query for support of an interface
  33. * @param _interfaceId The interface identifier, as specified in ERC-165
  34. * @return true if the contract at _address indicates support of the interface with
  35. * identifier _interfaceId, false otherwise
  36. * @dev Interface identification is specified in ERC-165.
  37. */
  38. function supportsInterface(address _address, bytes4 _interfaceId)
  39. internal
  40. view
  41. returns (bool)
  42. {
  43. // query support of both ERC165 as per the spec and support of _interfaceId
  44. return supportsERC165(_address) &&
  45. supportsERC165Interface(_address, _interfaceId);
  46. }
  47. /**
  48. * @notice Query if a contract implements interfaces, also checks support of ERC165
  49. * @param _address The address of the contract to query for support of an interface
  50. * @param _interfaceIds A list of interface identifiers, as specified in ERC-165
  51. * @return true if the contract at _address indicates support all interfaces in the
  52. * _interfaceIds list, false otherwise
  53. * @dev Interface identification is specified in ERC-165.
  54. */
  55. function supportsInterfaces(address _address, bytes4[] _interfaceIds)
  56. internal
  57. view
  58. returns (bool)
  59. {
  60. // query support of ERC165 itself
  61. if (!supportsERC165(_address)) {
  62. return false;
  63. }
  64. // query support of each interface in _interfaceIds
  65. for (uint256 i = 0; i < _interfaceIds.length; i++) {
  66. if (!supportsERC165Interface(_address, _interfaceIds[i])) {
  67. return false;
  68. }
  69. }
  70. // all interfaces supported
  71. return true;
  72. }
  73. /**
  74. * @notice Query if a contract implements an interface, does not check ERC165 support
  75. * @param _address The address of the contract to query for support of an interface
  76. * @param _interfaceId The interface identifier, as specified in ERC-165
  77. * @return true if the contract at _address indicates support of the interface with
  78. * identifier _interfaceId, false otherwise
  79. * @dev Assumes that _address contains a contract that supports ERC165, otherwise
  80. * the behavior of this method is undefined. This precondition can be checked
  81. * with the `supportsERC165` method in this library.
  82. * Interface identification is specified in ERC-165.
  83. */
  84. function supportsERC165Interface(address _address, bytes4 _interfaceId)
  85. private
  86. view
  87. returns (bool)
  88. {
  89. // success determines whether the staticcall succeeded and result determines
  90. // whether the contract at _address indicates support of _interfaceId
  91. (bool success, bool result) = callERC165SupportsInterface(
  92. _address, _interfaceId);
  93. return (success && result);
  94. }
  95. /**
  96. * @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
  97. * @param _address The address of the contract to query for support of an interface
  98. * @param _interfaceId The interface identifier, as specified in ERC-165
  99. * @return success true if the STATICCALL succeeded, false otherwise
  100. * @return result true if the STATICCALL succeeded and the contract at _address
  101. * indicates support of the interface with identifier _interfaceId, false otherwise
  102. */
  103. function callERC165SupportsInterface(
  104. address _address,
  105. bytes4 _interfaceId
  106. )
  107. private
  108. view
  109. returns (bool success, bool result)
  110. {
  111. bytes memory encodedParams = abi.encodeWithSelector(
  112. InterfaceId_ERC165,
  113. _interfaceId
  114. );
  115. // solium-disable-next-line security/no-inline-assembly
  116. assembly {
  117. let encodedParams_data := add(0x20, encodedParams)
  118. let encodedParams_size := mload(encodedParams)
  119. let output := mload(0x40) // Find empty storage location using "free memory pointer"
  120. mstore(output, 0x0)
  121. success := staticcall(
  122. 30000, // 30k gas
  123. _address, // To addr
  124. encodedParams_data,
  125. encodedParams_size,
  126. output,
  127. 0x20 // Outputs are 32 bytes long
  128. )
  129. result := mload(output) // Load the result
  130. }
  131. }
  132. }