Skip to content

Commit

Permalink
Add ERC165Checker.getSupportedInterfaces (#2469)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
5 people committed Jan 19, 2021
1 parent 9e49be4 commit c2c08af
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
23 changes: 23 additions & 0 deletions contracts/introspection/ERC165Checker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions contracts/mocks/ERC165CheckerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 52 additions & 0 deletions test/introspection/ERC165Checker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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 () {
Expand Down Expand Up @@ -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 () {
Expand All @@ -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);
});
});
});

0 comments on commit c2c08af

Please sign in to comment.