ERC165Checker.sol 4.9 KB

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