ERC165Checker.sol 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. pragma solidity ^0.5.2;
  2. /**
  3. * @title ERC165Checker
  4. * @dev Use `using ERC165Checker for address`; to include this library
  5. * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
  6. */
  7. library ERC165Checker {
  8. // As per the EIP-165 spec, no interface should ever match 0xffffffff
  9. bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
  10. bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
  11. /**
  12. * 0x01ffc9a7 ===
  13. * bytes4(keccak256('supportsInterface(bytes4)'))
  14. */
  15. /**
  16. * @notice Query if a contract supports ERC165
  17. * @param account The address of the contract to query for support of ERC165
  18. * @return true if the contract at account implements ERC165
  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. * @notice Query if a contract implements an interface, also checks support of ERC165
  28. * @param account The address of the contract to query for support of an interface
  29. * @param interfaceId The interface identifier, as specified in ERC-165
  30. * @return true if the contract at account indicates support of the interface with
  31. * identifier interfaceId, false otherwise
  32. * @dev Interface identification is specified in ERC-165.
  33. */
  34. function _supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
  35. // query support of both ERC165 as per the spec and support of _interfaceId
  36. return _supportsERC165(account) &&
  37. _supportsERC165Interface(account, interfaceId);
  38. }
  39. /**
  40. * @notice Query if a contract implements interfaces, also checks support of ERC165
  41. * @param account The address of the contract to query for support of an interface
  42. * @param interfaceIds A list of interface identifiers, as specified in ERC-165
  43. * @return true if the contract at account indicates support all interfaces in the
  44. * interfaceIds list, false otherwise
  45. * @dev Interface identification is specified in ERC-165.
  46. */
  47. function _supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
  48. // query support of ERC165 itself
  49. if (!_supportsERC165(account)) {
  50. return false;
  51. }
  52. // query support of each interface in _interfaceIds
  53. for (uint256 i = 0; i < interfaceIds.length; i++) {
  54. if (!_supportsERC165Interface(account, interfaceIds[i])) {
  55. return false;
  56. }
  57. }
  58. // all interfaces supported
  59. return true;
  60. }
  61. /**
  62. * @notice Query if a contract implements an interface, does not check ERC165 support
  63. * @param account The address of the contract to query for support of an interface
  64. * @param interfaceId The interface identifier, as specified in ERC-165
  65. * @return true if the contract at account indicates support of the interface with
  66. * identifier interfaceId, false otherwise
  67. * @dev Assumes that account contains a contract that supports ERC165, otherwise
  68. * the behavior of this method is undefined. This precondition can be checked
  69. * with the `supportsERC165` method in this library.
  70. * Interface identification is specified in ERC-165.
  71. */
  72. function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
  73. // success determines whether the staticcall succeeded and result determines
  74. // whether the contract at account indicates support of _interfaceId
  75. (bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);
  76. return (success && result);
  77. }
  78. /**
  79. * @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
  80. * @param account The address of the contract to query for support of an interface
  81. * @param interfaceId The interface identifier, as specified in ERC-165
  82. * @return success true if the STATICCALL succeeded, false otherwise
  83. * @return result true if the STATICCALL succeeded and the contract at account
  84. * indicates support of the interface with identifier interfaceId, false otherwise
  85. */
  86. function _callERC165SupportsInterface(address account, bytes4 interfaceId)
  87. private
  88. view
  89. returns (bool success, bool result)
  90. {
  91. bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);
  92. // solhint-disable-next-line no-inline-assembly
  93. assembly {
  94. let encodedParams_data := add(0x20, encodedParams)
  95. let encodedParams_size := mload(encodedParams)
  96. let output := mload(0x40) // Find empty storage location using "free memory pointer"
  97. mstore(output, 0x0)
  98. success := staticcall(
  99. 30000, // 30k gas
  100. account, // To addr
  101. encodedParams_data,
  102. encodedParams_size,
  103. output,
  104. 0x20 // Outputs are 32 bytes long
  105. )
  106. result := mload(output) // Load the result
  107. }
  108. }
  109. }