ERC165Checker.sol 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165Checker.sol)
  3. pragma solidity ^0.8.20;
  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 ERC-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 ERC-165 must explicitly indicate support of
  20. // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
  21. if (supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId)) {
  22. (bool success, bool supported) = _trySupportsInterface(account, INTERFACE_ID_INVALID);
  23. return success && !supported;
  24. } else {
  25. return false;
  26. }
  27. }
  28. /**
  29. * @dev Returns true if `account` supports the interface defined by
  30. * `interfaceId`. Support for {IERC165} itself is queried automatically.
  31. *
  32. * See {IERC165-supportsInterface}.
  33. */
  34. function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
  35. // query support of both ERC-165 as per the spec and support of _interfaceId
  36. return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
  37. }
  38. /**
  39. * @dev Returns a boolean array where each value corresponds to the
  40. * interfaces passed in and whether they're supported or not. This allows
  41. * you to batch check interfaces for a contract where your expectation
  42. * is that some interfaces may not be supported.
  43. *
  44. * See {IERC165-supportsInterface}.
  45. */
  46. function getSupportedInterfaces(
  47. address account,
  48. bytes4[] memory interfaceIds
  49. ) internal view returns (bool[] memory) {
  50. // an array of booleans corresponding to interfaceIds and whether they're supported or not
  51. bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
  52. // query support of ERC-165 itself
  53. if (supportsERC165(account)) {
  54. // query support of each interface in interfaceIds
  55. for (uint256 i = 0; i < interfaceIds.length; i++) {
  56. interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
  57. }
  58. }
  59. return interfaceIdsSupported;
  60. }
  61. /**
  62. * @dev Returns true if `account` supports all the interfaces defined in
  63. * `interfaceIds`. Support for {IERC165} itself is queried automatically.
  64. *
  65. * Batch-querying can lead to gas savings by skipping repeated checks for
  66. * {IERC165} support.
  67. *
  68. * See {IERC165-supportsInterface}.
  69. */
  70. function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
  71. // query support of ERC-165 itself
  72. if (!supportsERC165(account)) {
  73. return false;
  74. }
  75. // query support of each interface in interfaceIds
  76. for (uint256 i = 0; i < interfaceIds.length; i++) {
  77. if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
  78. return false;
  79. }
  80. }
  81. // all interfaces supported
  82. return true;
  83. }
  84. /**
  85. * @notice Query if a contract implements an interface, does not check ERC-165 support
  86. * @param account The address of the contract to query for support of an interface
  87. * @param interfaceId The interface identifier, as specified in ERC-165
  88. * @return true if the contract at account indicates support of the interface with
  89. * identifier interfaceId, false otherwise
  90. * @dev Assumes that account contains a contract that supports ERC-165, otherwise
  91. * the behavior of this method is undefined. This precondition can be checked
  92. * with {supportsERC165}.
  93. *
  94. * Some precompiled contracts will falsely indicate support for a given interface, so caution
  95. * should be exercised when using this function.
  96. *
  97. * Interface identification is specified in ERC-165.
  98. */
  99. function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
  100. (bool success, bool supported) = _trySupportsInterface(account, interfaceId);
  101. return success && supported;
  102. }
  103. /**
  104. * @dev Attempts to call `supportsInterface` on a contract and returns both the call
  105. * success status and the interface support result.
  106. *
  107. * This function performs a low-level static call to the contract's `supportsInterface`
  108. * function. It returns:
  109. *
  110. * * `success`: true if the call didn't revert, false if it did
  111. * * `supported`: true if the call succeeded AND returned data indicating the interface is supported
  112. */
  113. function _trySupportsInterface(
  114. address account,
  115. bytes4 interfaceId
  116. ) private view returns (bool success, bool supported) {
  117. bytes4 selector = IERC165.supportsInterface.selector;
  118. assembly ("memory-safe") {
  119. mstore(0x00, selector)
  120. mstore(0x04, interfaceId)
  121. success := staticcall(30000, account, 0x00, 0x24, 0x00, 0x20)
  122. supported := and(
  123. gt(returndatasize(), 0x1F), // we have at least 32 bytes of returndata
  124. iszero(iszero(mload(0x00))) // the first 32 bytes of returndata are non-zero
  125. )
  126. }
  127. }
  128. }