Skip to content

Commit

Permalink
Enable using ERC165 check for one supported interface directly (#3339)
Browse files Browse the repository at this point in the history
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: Francisco <frangio.1@gmail.com>
  • Loading branch information
3 people committed Jun 27, 2022
1 parent 40172c2 commit e734b42
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions contracts/mocks/ERC165CheckerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
12 changes: 6 additions & 6 deletions contracts/utils/introspection/ERC165Checker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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]);
}
}

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

0 comments on commit e734b42

Please sign in to comment.