ERC165Checker.sol 4.4 KB

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