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;
}
}
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?
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.
@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.
@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 signatureabi.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 invokingexternal 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.
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.