Przeglądaj źródła

Add ERC165Checker.getSupportedInterfaces (#2469)

Co-authored-by: conspyrosy <1027439+Spyros-Stylianou@users.noreply.github.com>
Co-authored-by: kamiebisu <kamiebisu@protonmail.com>
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
Conspyrosy 4 lat temu
rodzic
commit
c2c08af16d

+ 1 - 0
CHANGELOG.md

@@ -13,6 +13,7 @@
  * `UpgradeableProxy`: bubble revert reasons from initialization calls. ([#2454](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2454))
  * `SafeMath`: fix a memory allocation issue by adding new `SafeMath.tryOp(uint,uint)→(bool,uint)` functions. `SafeMath.op(uint,uint,string)→uint` are now deprecated. ([#2462](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2462))
  * `EnumerableMap`: fix a memory allocation issue by adding new `EnumerableMap.tryGet(uint)→(bool,address)` functions. `EnumerableMap.get(uint)→string` is now deprecated. ([#2462](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2462))
+ * `ERC165Checker`: added batch `getSupportedInterfaces`. ([#2469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2469))
 
 ## 3.3.0 (2020-11-26)
 

+ 23 - 0
contracts/introspection/ERC165Checker.sol

@@ -40,6 +40,29 @@ library ERC165Checker {
             _supportsERC165Interface(account, interfaceId);
     }
 
+    /**
+     * @dev Returns a boolean array where each value corresponds to the
+     * interfaces passed in and whether they're supported or not. This allows
+     * you to batch check interfaces for a contract where your expectation
+     * is that some interfaces may not be supported.
+     *
+     * See {IERC165-supportsInterface}.
+     */
+    function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) {
+        // an array of booleans corresponding to interfaceIds and whether they're supported or not
+        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
+
+        // query support of ERC165 itself
+        if (supportsERC165(account)) {
+            // query support of each interface in interfaceIds
+            for (uint256 i = 0; i < interfaceIds.length; i++) {
+                interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
+            }
+        }
+
+        return interfaceIdsSupported;
+    }
+
     /**
      * @dev Returns true if `account` supports all the interfaces defined in
      * `interfaceIds`. Support for {IERC165} itself is queried automatically.

+ 4 - 0
contracts/mocks/ERC165CheckerMock.sol

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

+ 52 - 0
test/introspection/ERC165Checker.test.js

@@ -37,6 +37,12 @@ contract('ERC165Checker', function (accounts) {
       const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]);
       expect(supported).to.equal(false);
     });
+
+    it('does not support mock interface via getSupportedInterfaces', async function () {
+      const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]);
+      expect(supported.length).to.equal(1);
+      expect(supported[0]).to.equal(false);
+    });
   });
 
   context('ERC165 supported', function () {
@@ -58,6 +64,12 @@ contract('ERC165Checker', function (accounts) {
       const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]);
       expect(supported).to.equal(false);
     });
+
+    it('does not support mock interface via getSupportedInterfaces', async function () {
+      const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]);
+      expect(supported.length).to.equal(1);
+      expect(supported[0]).to.equal(false);
+    });
   });
 
   context('ERC165 and single interface supported', function () {
@@ -79,6 +91,12 @@ contract('ERC165Checker', function (accounts) {
       const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]);
       expect(supported).to.equal(true);
     });
+
+    it('supports mock interface via getSupportedInterfaces', async function () {
+      const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]);
+      expect(supported.length).to.equal(1);
+      expect(supported[0]).to.equal(true);
+    });
   });
 
   context('ERC165 and many interfaces supported', function () {
@@ -117,6 +135,34 @@ contract('ERC165Checker', function (accounts) {
       const supported = await this.mock.supportsAllInterfaces(this.target.address, interfaceIdsToTest);
       expect(supported).to.equal(false);
     });
+
+    it('supports all interfaceIds via getSupportedInterfaces', async function () {
+      const supported = await this.mock.getSupportedInterfaces(this.target.address, this.supportedInterfaces);
+      expect(supported.length).to.equal(3);
+      expect(supported[0]).to.equal(true);
+      expect(supported[1]).to.equal(true);
+      expect(supported[2]).to.equal(true);
+    });
+
+    it('supports none of the interfaces queried via getSupportedInterfaces', async function () {
+      const interfaceIdsToTest = [DUMMY_UNSUPPORTED_ID, DUMMY_UNSUPPORTED_ID_2];
+
+      const supported = await this.mock.getSupportedInterfaces(this.target.address, interfaceIdsToTest);
+      expect(supported.length).to.equal(2);
+      expect(supported[0]).to.equal(false);
+      expect(supported[1]).to.equal(false);
+    });
+
+    it('supports not all of the interfaces queried via getSupportedInterfaces', async function () {
+      const interfaceIdsToTest = [...this.supportedInterfaces, DUMMY_UNSUPPORTED_ID];
+
+      const supported = await this.mock.getSupportedInterfaces(this.target.address, interfaceIdsToTest);
+      expect(supported.length).to.equal(4);
+      expect(supported[0]).to.equal(true);
+      expect(supported[1]).to.equal(true);
+      expect(supported[2]).to.equal(true);
+      expect(supported[3]).to.equal(false);
+    });
   });
 
   context('account address does not support ERC165', function () {
@@ -134,5 +180,11 @@ contract('ERC165Checker', function (accounts) {
       const supported = await this.mock.supportsAllInterfaces(DUMMY_ACCOUNT, [DUMMY_ID]);
       expect(supported).to.equal(false);
     });
+
+    it('does not support mock interface via getSupportedInterfaces', async function () {
+      const supported = await this.mock.getSupportedInterfaces(DUMMY_ACCOUNT, [DUMMY_ID]);
+      expect(supported.length).to.equal(1);
+      expect(supported[0]).to.equal(false);
+    });
   });
 });