-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable using ERC165 check for one supported interface directly #3339
Enable using ERC165 check for one supported interface directly #3339
Conversation
Hello @ashhanai ERC165 dictates that we should check As I understand your usecase would be to start by checking ERC165 compliance, then check specific interfaces, wich different error depending in which of the two fail. Can you tell us more about the context where you want to do that? |
Hi @Amxx, I think you get my point. What I am trying to do is to be able to check list of supported interfaces, but end as soon as I find first match. The flow could look something like this:
With current Or I can try to use I believe this would be very useful for contracts working with mutually exclusive ERC tokens (ERC20, ERC721, ERC1155, ...). |
What about à function that given an array of interfaces returns a yes/no array ? |
That's the |
Right, the |
If we talk about mutually exclusive interfaces, If we talk about inclusive interfaces (like ERC20, ERC777, ERC2612, ...), as these interfaces doesn't force implementing ERC165 in their EIPs, it's handy to know if it's missing ERC165 or the token really don't implement any of these ERCs. If we just take that they don't implement those ERCs just because What about creating a new function, that would be very similar to Also it could take flag that would determine if function should stop on a first match or continue (for mutually exclusive interfaces). |
I think having a
could make sense. |
@Amxx That is not what @ashhanai is asking for though, because it still checks the entire array. The use case is to find the first supported interface from a list. I think this feels a little niche to support out of the box, so I'm considering actually making the private function internal. We should give it a scary name so that people don't use it unless they absolutely want to. |
I agree with @frangio + I also think returning the additional |
Hey @Amxx @frangio 👋 |
@frangio @Amxx Looking at the function body: function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);
// if the contract being called does not implement `IERC165` this will fail.
(bool success, bytes memory result) = account.staticcall{gas: 30000}(encodedParams);
if (result.length < 32) return false;
return success && abi.decode(result, (bool));
} The function I have written an example below and tested in Remix. // SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/utils/introspection/ERC165Checker.sol";
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/utils/introspection/ERC165.sol";
contract Playground {
function _supportsERC165Interface(address account, bytes4 interfaceId) private 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;
return success && abi.decode(result, (bool));
}
function testSupportsInterface(address _target) public view returns (bool) {
return _supportsERC165Interface(_target, 0xaabbccdd);
}
}
// _supportsERC165Interface(0xaabbccdd) --> returns true
contract TargetWithERC165 is ERC165 {
function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
return interfaceId == 0xaabbccdd;
}
}
// _supportsERC165Interface(0xaabbccdd) --> returns false
contract TargetWithoutERC165 {
}
// _supportsERC165Interface(0xaabbccdd) --> returns false
contract TargetWithoutERC165ButFallback {
fallback() external {
}
} |
Should we take this occasion to refactor it to
? Edit: that would revert for EOA, when we need it to return false :/ |
if we make We could rename it to Any suggestions? |
Nothing in my mind, maybe |
@Amxx can we keep the |
If it's an internal function in a library, the convention is no. we would have to make an exception ... I don't like that. |
What do you think of "raw" for the name? |
1 similar comment
What do you think of "raw" for the name? |
Good Suggestion 👍 |
Lets go for |
We need a changelog entry |
A bit late to the naming party. I was about to suggest Was thinking that could also look similar to the concept of I guess "raw" is smaller and might read better :) |
I like the |
Lets go with @ashhanai can you do that change and add a Changelog entry please? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
…eppelin#3339) Co-authored-by: Hadrien Croubois <[email protected]> Co-authored-by: Francisco <[email protected]>
This PR changes function visibility of
ERC165Checker._supportsERC165Interface
from private to internal. The function will be callable from contract using this library directly.Rationale
Existing function doesn't return an information whether token doesn't implement ERC165 or given interface. If contract need this information, it need to call
supportsERC165
first and thensupportsInterface
orgetSupportedInterfaces
. Unfortunately that would call ERC165 check twice.Solution
Use existing function
_supportsERC165Interface
and enable it to be called by contract, not just library. Keep the underscore to indicate that this function is not meant to be called directly by default, developer has to know what he is doing and to check ERC165 support prior to calling this function.PR Checklist