-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(world,world-modules): requireInterface correctly specifies ERC165…
… [M-02] (#2016)
- Loading branch information
Showing
5 changed files
with
143 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@latticexyz/world": patch | ||
--- | ||
|
||
Fixed `requireInterface` to correctly specify ERC165. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// SPDX-License-Identifier: MIT | ||
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165Checker.sol) | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
import { IERC165 } from "./IERC165.sol"; | ||
|
||
/** | ||
* @dev Library used to query support of an interface declared via {IERC165}. | ||
* | ||
* Note that these functions return the actual result of the query: they do not | ||
* `revert` if an interface is not supported. It is up to the caller to decide | ||
* what to do in these cases. | ||
*/ | ||
library ERC165Checker { | ||
// As per the EIP-165 spec, no interface should ever match 0xffffffff | ||
bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff; | ||
|
||
/** | ||
* @dev Returns true if `account` supports the {IERC165} interface. | ||
*/ | ||
function supportsERC165(address account) internal view returns (bool) { | ||
// Any contract that implements ERC165 must explicitly indicate support of | ||
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid | ||
return | ||
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) && | ||
!supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID); | ||
} | ||
|
||
/** | ||
* @dev Returns true if `account` supports the interface defined by | ||
* `interfaceId`. Support for {IERC165} itself is queried automatically. | ||
* | ||
* See {IERC165-supportsInterface}. | ||
*/ | ||
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) && supportsERC165InterfaceUnchecked(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] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]); | ||
} | ||
} | ||
|
||
return interfaceIdsSupported; | ||
} | ||
|
||
/** | ||
* @dev Returns true if `account` supports all the interfaces defined in | ||
* `interfaceIds`. Support for {IERC165} itself is queried automatically. | ||
* | ||
* Batch-querying can lead to gas savings by skipping repeated checks for | ||
* {IERC165} support. | ||
* | ||
* See {IERC165-supportsInterface}. | ||
*/ | ||
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { | ||
// query support of ERC165 itself | ||
if (!supportsERC165(account)) { | ||
return false; | ||
} | ||
|
||
// query support of each interface in interfaceIds | ||
for (uint256 i = 0; i < interfaceIds.length; i++) { | ||
if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) { | ||
return false; | ||
} | ||
} | ||
|
||
// all interfaces supported | ||
return true; | ||
} | ||
|
||
/** | ||
* @notice Query if a contract implements an interface, does not check ERC165 support | ||
* @param account The address of the contract to query for support of an interface | ||
* @param interfaceId The interface identifier, as specified in ERC-165 | ||
* @return true if the contract at account indicates support of the interface with | ||
* identifier interfaceId, false otherwise | ||
* @dev Assumes that account contains a contract that supports ERC165, otherwise | ||
* the behavior of this method is undefined. This precondition can be checked | ||
* with {supportsERC165}. | ||
* | ||
* Some precompiled contracts will falsely indicate support for a given interface, so caution | ||
* should be exercised when using this function. | ||
* | ||
* Interface identification is specified in ERC-165. | ||
*/ | ||
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) { | ||
// prepare call | ||
bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId)); | ||
|
||
// perform static call | ||
bool success; | ||
uint256 returnSize; | ||
uint256 returnValue; | ||
assembly { | ||
success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20) | ||
returnSize := returndatasize() | ||
returnValue := mload(0x00) | ||
} | ||
|
||
return success && returnSize >= 0x20 && returnValue > 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters