Skip to content
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

Allow selectors to be used in constants assignment and support overloading #7922

Closed
k06a opened this issue Dec 9, 2019 · 8 comments
Closed

Comments

@k06a
Copy link

k06a commented Dec 9, 2019

Abstract

For example ERC721 contains:

/*
 *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
 *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
 *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
 *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
 *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
 *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
 *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
 *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
 *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
 *
 *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
 *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
 */
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;

But could contain:

bytes4 private constant _INTERFACE_ID_ERC721 = 0
    ^ balanceOf(address).selector
    ^ ownerOf(uint256).selector
    ^ approve(address,uint256).selector
    ^ getApproved(uint256).selector
    ^ setApprovalForAll(address,bool).selector
    ^ isApprovedForAll(address,address).selector
    ^ transferFrom(address,address,uint256).selector
    ^ safeTransferFrom(address,address,uint256).selector
    ^ safeTransferFrom(address,address,uint256,bytes).selector;

Motivation

Avoiding off-code computations.

@erak erak added the to discuss label Dec 9, 2019
@erak
Copy link
Collaborator

erak commented Dec 9, 2019

@k06a Thanks for bringing this up. In general, I think that this would be a good reason to re-think member lookup for functions and research if a new syntax, that includes types to be used during overload resolution, can be introduced. You can almost express it in a non-constant way:

contract ERC721 {
    bytes4 private _INTERFACE_ID_ERC721 = runtimeId();
    
    function balanceOf(address) public { }
    function ownerOf(uint256) public { }
    function approve(address,uint256) public { }
    function getApproved(uint256) public { }
    function setApprovalForAll(address,bool) public { }
    function isApprovedForAll(address,address) public { }
    function transferFrom(address,address,uint256) public { }
    function safeTransferFrom(address,address,uint256) external { }
    function safeTransferFromWithData(address,address,uint256,bytes memory) public { }
    
    function runtimeId() public pure returns (bytes4) {
        return this.balanceOf.selector
            ^ this.ownerOf.selector
            ^ this.approve.selector
            ^ this.getApproved.selector
            ^ this.setApprovalForAll.selector
            ^ this.isApprovedForAll.selector
            ^ this.transferFrom.selector
            ^ this.safeTransferFrom.selector
            ^ this.safeTransferFromWithData.selector;
    }
}

but as you might see, the second overloaded version of safeTransferFrom has to be renamed to safeTransferFromWithData, because overload resolution does not take the additional parameter into account.

The syntax you've proposed is already used for an actual function call, so it has to be different. Also the aspect that the ID should be compile-time constant needs to be taken into account.

Of course, you could also calculate them on-chain, which would in turn increase gas costs:

function runtimeIdHashed() public pure returns (bytes4) {
    return bytes4(keccak256("balanceOf(address)"))
        ^ bytes4(keccak256("ownerOf(uint256)"))
        ^ ...;
}

@erak
Copy link
Collaborator

erak commented Dec 9, 2019

@k06a So bottom line is: I think we'd need a different proposal for the syntax :-)

@chriseth
Copy link
Contributor

chriseth commented Dec 9, 2019

We might also consider native support for interface identifiers.

@chriseth
Copy link
Contributor

chriseth commented Dec 9, 2019

#7856

@k06a
Copy link
Author

k06a commented Dec 9, 2019

@erak what about this syntax?

this.selectors.f()
this.selectors.f(uint256)

Or use mandatory void for functions without arguments:

this.f(void).selector
this.f(uint256).selector

So you will be able to distinct calls from selectors by arguments: types or expressions.

@chriseth
Copy link
Contributor

Maybe we could use this.f{} and this.f{uint256} as we also use { and } for #2136

@ekpyron
Copy link
Member

ekpyron commented Jan 13, 2020

function(uint256)external(this.f)... like casting to the required function type as in C++ - but in solidity it's indeed extremely horrible...
this.f["(uint256)"] or maybe better this.f[(uint256)], i.e. this.f could be a compile-time-only mapping from TypeType to FunctionType... that'd make sense, but it doesn't look nice either...

@cameel
Copy link
Member

cameel commented Sep 26, 2022

Closing this since selectors are usable in constant assignments since 0.6.3 and we already have an issue covering selectors of overloaded functions: #3556.

Also, I think that the introduction of interfaceId in #7856 fulfilled the original motivation behind this issue.

@cameel cameel closed this as completed Sep 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants