ERC165Checker.sol 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. pragma solidity ^0.5.0;
  2. /**
  3. * @dev Library used to query support of an interface declared via `IERC165`.
  4. *
  5. * Note that these functions return the actual result of the query: they do not
  6. * `revert` if an interface is not supported. It is up to the caller to decide
  7. * what to do in these cases.
  8. */
  9. library ERC165Checker {
  10. // As per the EIP-165 spec, no interface should ever match 0xffffffff
  11. bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
  12. /*
  13. * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
  14. */
  15. bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
  16. /**
  17. * @dev Returns true if `account` supports the `IERC165` interface,
  18. */
  19. function _supportsERC165(address account) internal view returns (bool) {
  20. // Any contract that implements ERC165 must explicitly indicate support of
  21. // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
  22. return _supportsERC165Interface(account, _INTERFACE_ID_ERC165) &&
  23. !_supportsERC165Interface(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) &&
  34. _supportsERC165Interface(account, interfaceId);
  35. }
  36. /**
  37. * @dev Returns true if `account` supports all the interfaces defined in
  38. * `interfaceIds`. Support for `IERC165` itself is queried automatically.
  39. *
  40. * Batch-querying can lead to gas savings by skipping repeated checks for
  41. * `IERC165` support.
  42. *
  43. * See `IERC165.supportsInterface`.
  44. */
  45. function _supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
  46. // query support of ERC165 itself
  47. if (!_supportsERC165(account)) {
  48. return false;
  49. }
  50. // query support of each interface in _interfaceIds
  51. for (uint256 i = 0; i < interfaceIds.length; i++) {
  52. if (!_supportsERC165Interface(account, interfaceIds[i])) {
  53. return false;
  54. }
  55. }
  56. // all interfaces supported
  57. return true;
  58. }
  59. /**
  60. * @notice Query if a contract implements an interface, does not check ERC165 support
  61. * @param account The address of the contract to query for support of an interface
  62. * @param interfaceId The interface identifier, as specified in ERC-165
  63. * @return true if the contract at account indicates support of the interface with
  64. * identifier interfaceId, false otherwise
  65. * @dev Assumes that account contains a contract that supports ERC165, otherwise
  66. * the behavior of this method is undefined. This precondition can be checked
  67. * with the `supportsERC165` method in this library.
  68. * Interface identification is specified in ERC-165.
  69. */
  70. function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
  71. // success determines whether the staticcall succeeded and result determines
  72. // whether the contract at account indicates support of _interfaceId
  73. (bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);
  74. return (success && result);
  75. }
  76. /**
  77. * @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
  78. * @param account The address of the contract to query for support of an interface
  79. * @param interfaceId The interface identifier, as specified in ERC-165
  80. * @return success true if the STATICCALL succeeded, false otherwise
  81. * @return result true if the STATICCALL succeeded and the contract at account
  82. * indicates support of the interface with identifier interfaceId, false otherwise
  83. */
  84. function _callERC165SupportsInterface(address account, bytes4 interfaceId)
  85. private
  86. view
  87. returns (bool success, bool result)
  88. {
  89. bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);
  90. // solhint-disable-next-line no-inline-assembly
  91. assembly {
  92. let encodedParams_data := add(0x20, encodedParams)
  93. let encodedParams_size := mload(encodedParams)
  94. let output := mload(0x40) // Find empty storage location using "free memory pointer"
  95. mstore(output, 0x0)
  96. success := staticcall(
  97. 30000, // 30k gas
  98. account, // To addr
  99. encodedParams_data,
  100. encodedParams_size,
  101. output,
  102. 0x20 // Outputs are 32 bytes long
  103. )
  104. result := mload(output) // Load the result
  105. }
  106. }
  107. }