فهرست منبع

Enable using ERC165 check for one supported interface directly (#3339)

Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: Francisco <frangio.1@gmail.com>
ashhanai 3 سال پیش
والد
کامیت
e734b42fc2

+ 1 - 0
CHANGELOG.md

@@ -20,6 +20,7 @@
  * `PaymentSplitter`: add `releasable` getters. ([#3350](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3350))
  * `Initializable`: refactored implementation of modifiers for easier understanding. ([#3450](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3450))
  * `Proxies`: remove runtime check of ERC1967 storage slots. ([#3455](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3455))
+ * `ERC165Checker`: add `supportsERC165InterfaceUnchecked` for consulting individual interfaces without the full ERC165 protocol. ([#3339](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3339))
  * `Address`: optimize `functionCall` functions by checking contract size only if there is no returned data. ([#3469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3469))
 
 ### Breaking changes

+ 4 - 0
contracts/mocks/ERC165CheckerMock.sol

@@ -22,4 +22,8 @@ contract ERC165CheckerMock {
     function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool[] memory) {
         return account.getSupportedInterfaces(interfaceIds);
     }
+
+    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) public view returns (bool) {
+        return account.supportsERC165InterfaceUnchecked(interfaceId);
+    }
 }

+ 6 - 6
contracts/utils/introspection/ERC165Checker.sol

@@ -23,8 +23,8 @@ library ERC165Checker {
         // Any contract that implements ERC165 must explicitly indicate support of
         // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
         return
-            _supportsERC165Interface(account, type(IERC165).interfaceId) &&
-            !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
+            supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
+            !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
     }
 
     /**
@@ -35,7 +35,7 @@ library ERC165Checker {
      */
     function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
         // query support of both ERC165 as per the spec and support of _interfaceId
-        return supportsERC165(account) && _supportsERC165Interface(account, interfaceId);
+        return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
     }
 
     /**
@@ -60,7 +60,7 @@ library ERC165Checker {
         if (supportsERC165(account)) {
             // query support of each interface in interfaceIds
             for (uint256 i = 0; i < interfaceIds.length; i++) {
-                interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
+                interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
             }
         }
 
@@ -84,7 +84,7 @@ library ERC165Checker {
 
         // query support of each interface in _interfaceIds
         for (uint256 i = 0; i < interfaceIds.length; i++) {
-            if (!_supportsERC165Interface(account, interfaceIds[i])) {
+            if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
                 return false;
             }
         }
@@ -104,7 +104,7 @@ library ERC165Checker {
      * with {supportsERC165}.
      * Interface identification is specified in ERC-165.
      */
-    function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
+    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
         bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);
         (bool success, bytes memory result) = account.staticcall{gas: 30000}(encodedParams);
         if (result.length < 32) return false;

+ 32 - 0
test/utils/introspection/ERC165Checker.test.js

@@ -44,6 +44,11 @@ contract('ERC165Checker', function (accounts) {
       expect(supported.length).to.equal(1);
       expect(supported[0]).to.equal(false);
     });
+
+    it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () {
+      const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID);
+      expect(supported).to.equal(false);
+    });
   });
 
   context('ERC165 not supported', function () {
@@ -71,6 +76,11 @@ contract('ERC165Checker', function (accounts) {
       expect(supported.length).to.equal(1);
       expect(supported[0]).to.equal(false);
     });
+
+    it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () {
+      const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID);
+      expect(supported).to.equal(false);
+    });
   });
 
   context('ERC165 supported', function () {
@@ -98,6 +108,11 @@ contract('ERC165Checker', function (accounts) {
       expect(supported.length).to.equal(1);
       expect(supported[0]).to.equal(false);
     });
+
+    it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () {
+      const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID);
+      expect(supported).to.equal(false);
+    });
   });
 
   context('ERC165 and single interface supported', function () {
@@ -125,6 +140,11 @@ contract('ERC165Checker', function (accounts) {
       expect(supported.length).to.equal(1);
       expect(supported[0]).to.equal(true);
     });
+
+    it('supports mock interface via supportsERC165InterfaceUnchecked', async function () {
+      const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID);
+      expect(supported).to.equal(true);
+    });
   });
 
   context('ERC165 and many interfaces supported', function () {
@@ -191,6 +211,13 @@ contract('ERC165Checker', function (accounts) {
       expect(supported[2]).to.equal(true);
       expect(supported[3]).to.equal(false);
     });
+
+    it('supports each interfaceId via supportsERC165InterfaceUnchecked', async function () {
+      for (const interfaceId of this.supportedInterfaces) {
+        const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, interfaceId);
+        expect(supported).to.equal(true);
+      };
+    });
   });
 
   context('account address does not support ERC165', function () {
@@ -214,5 +241,10 @@ contract('ERC165Checker', function (accounts) {
       expect(supported.length).to.equal(1);
       expect(supported[0]).to.equal(false);
     });
+
+    it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () {
+      const supported = await this.mock.supportsERC165InterfaceUnchecked(DUMMY_ACCOUNT, DUMMY_ID);
+      expect(supported).to.equal(false);
+    });
   });
 });