Solidity 0.6: change address(f) to f.address for f being of external function type

https://solidity.readthedocs.io/en/latest/060-breaking-changes.html#how-to-update-your-code
change address(f) to f.address for f being of external function type.

@gitpusha asked in Telegram:

why you would convert an external function type to an address
Are there some efficiency gains to be made?

4 Likes

I found the GitHub issue https://github.com/ethereum/solidity/issues/4994

I am not sure of the use case for this.
I did a quick sample to show getting the address (and the selector) for a function.

pragma solidity ^0.5.0;

contract Test {
    function hello() public returns(string memory) {
        return "Hello";
    }
    
    function getAddress() public returns(address) {
        return address(this.hello);
    }
    
    function getSelector() public returns(bytes4) {
        return this.hello.selector;
    }
}
1 Like

I’m also unsure as to why you’d perform such an action. A function call having in address seems a bit strange, and goes against how people think of the language. From the discussion on the GitHub issue, it looks like they did this change simply because they could.

I take it the address evaluates to the contract’s address?

2 Likes

The following returned the address of the contract.

    function getAddress() public returns(address) {
        return address(this.hello);
    }
1 Like

This is a really obscure part of the language. :smiley:

I think the 0.6 change makes a lot of sense, and I don’t think they did it just because they could. Let me explain.

Solidity has first class functions, so you can take a function as a value, and for example store it in a variable. When you do this with external functions, the function value that you create is attached to a specific contract, kind of like using Function.prototype.bind in JavaScript. So the following works (and causes an infinite loop) by essentially creating a function value (this.foo) and then calling it:

function foo() external {
    (this.foo)();
}

Notice that when we call the value we don’t need to specify what contract to call it on, it is already stored within the function value itself.

Before 0.6, to obtain the address of the attached contract you had to do a cast operation, which is absolutely weird because it makes no sense to cast a function to a contract address. In 0.6 this actually looks like accessing a member of an object, so it really reflects much better the idea of obtaining the attached contract.

4 Likes

Thanks @frangio for the great explanation, now I understand. :pray:

1 Like

@frangio can you think of a scenario in which you’d want to pass external functions around?

I agree that casting to an address was strange, what I meant by ‘they did it because they could’ was that passing external calls is by itself very niche, and getting the address of their contract even more so. I doubt many people are aware of this feature (much less use it), so making this change is sort of meh.

2 Likes

@abcoathup thanks for posting this here!

@frangio thanks for your info. Quick question:
why did you write it like so (this.foo)(); and not just this.foo() ? I thought the latter is normal syntax for calling an external function that is on the calling contract.
I agree with @nventuro that I am still shaky on the exact use case for accessing an external function’s address member. Normally I would know the contract address, instantiate it in memory and call the function name on it or do a low level call on the contract address with the function selector or signature abi.encoded in the payload.

If the external function type would actually have its own address (which contradicts the findings of @abcoathup) I would have seen the contrived use case of invoking external functions directly on another contract by maybe doing a low level .call on that address instead of the contract’s address. This would maybe allow skipping abi.encodeWithSelector/abi.encodeWithSignature the payload because you can send the payload directly to that function by knowing its address. But this seems to be invalidated by @abcoathup findings that fn.address just returns its contract’s address.

1 Like

It was just to make the point that (this.foo) by itself is a value. Similar to writing var fooFunction = this.foo; fooFunction();, but I didn't want to write out the function type.

I suppose this feature can be useful for implementing a few scenarios with very dynamic behavior, but auditors would probably discourage it because it isn't commonly used or understood.

2 Likes