ERC165Checker.sol 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity >=0.6.2 <0.8.0;
  3. /**
  4. * @dev Library used to query support of an interface declared via {IERC165}.
  5. *
  6. * Note that these functions return the actual result of the query: they do not
  7. * `revert` if an interface is not supported. It is up to the caller to decide
  8. * what to do in these cases.
  9. */
  10. library ERC165Checker {
  11. // As per the EIP-165 spec, no interface should ever match 0xffffffff
  12. bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
  13. /*
  14. * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
  15. */
  16. bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
  17. /**
  18. * @dev Returns true if `account` supports the {IERC165} interface,
  19. */
  20. function supportsERC165(address account) internal view returns (bool) {
  21. // Any contract that implements ERC165 must explicitly indicate support of
  22. // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
  23. return _supportsERC165Interface(account, _INTERFACE_ID_ERC165) &&
  24. !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
  25. }
  26. /**
  27. * @dev Returns true if `account` supports the interface defined by
  28. * `interfaceId`. Support for {IERC165} itself is queried automatically.
  29. *
  30. * See {IERC165-supportsInterface}.
  31. */
  32. function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
  33. // query support of both ERC165 as per the spec and support of _interfaceId
  34. return supportsERC165(account) &&
  35. _supportsERC165Interface(account, interfaceId);
  36. }
  37. /**
  38. * @dev Returns a boolean array where each value corresponds to the
  39. * interfaces passed in and whether they're supported or not. This allows
  40. * you to batch check interfaces for a contract where your expectation
  41. * is that some interfaces may not be supported.
  42. *
  43. * See {IERC165-supportsInterface}.
  44. *
  45. * _Available since v3.4._
  46. */
  47. function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) {
  48. // an array of booleans corresponding to interfaceIds and whether they're supported or not
  49. bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
  50. // query support of ERC165 itself
  51. if (supportsERC165(account)) {
  52. // query support of each interface in interfaceIds
  53. for (uint256 i = 0; i < interfaceIds.length; i++) {
  54. interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
  55. }
  56. }
  57. return interfaceIdsSupported;
  58. }
  59. /**
  60. * @dev Returns true if `account` supports all the interfaces defined in
  61. * `interfaceIds`. Support for {IERC165} itself is queried automatically.
  62. *
  63. * Batch-querying can lead to gas savings by skipping repeated checks for
  64. * {IERC165} support.
  65. *
  66. * See {IERC165-supportsInterface}.
  67. */
  68. function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
  69. // query support of ERC165 itself
  70. if (!supportsERC165(account)) {
  71. return false;
  72. }
  73. // query support of each interface in _interfaceIds
  74. for (uint256 i = 0; i < interfaceIds.length; i++) {
  75. if (!_supportsERC165Interface(account, interfaceIds[i])) {
  76. return false;
  77. }
  78. }
  79. // all interfaces supported
  80. return true;
  81. }
  82. /**
  83. * @notice Query if a contract implements an interface, does not check ERC165 support
  84. * @param account The address of the contract to query for support of an interface
  85. * @param interfaceId The interface identifier, as specified in ERC-165
  86. * @return true if the contract at account indicates support of the interface with
  87. * identifier interfaceId, false otherwise
  88. * @dev Assumes that account contains a contract that supports ERC165, otherwise
  89. * the behavior of this method is undefined. This precondition can be checked
  90. * with {supportsERC165}.
  91. * Interface identification is specified in ERC-165.
  92. */
  93. function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
  94. // success determines whether the staticcall succeeded and result determines
  95. // whether the contract at account indicates support of _interfaceId
  96. (bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);
  97. return (success && result);
  98. }
  99. /**
  100. * @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
  101. * @param account The address of the contract to query for support of an interface
  102. * @param interfaceId The interface identifier, as specified in ERC-165
  103. * @return success true if the STATICCALL succeeded, false otherwise
  104. * @return result true if the STATICCALL succeeded and the contract at account
  105. * indicates support of the interface with identifier interfaceId, false otherwise
  106. */
  107. function _callERC165SupportsInterface(address account, bytes4 interfaceId)
  108. private
  109. view
  110. returns (bool, bool)
  111. {
  112. bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);
  113. (bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams);
  114. if (result.length < 32) return (false, false);
  115. return (success, abi.decode(result, (bool)));
  116. }
  117. }