A function selector allows you to perform dynamic invocation of a function, based on the name of the function and the type of each one of the input arguments.
For example, suppose you have:
contract Contract1 {
function func(uint256 x, uint8 y) public returns (uint32, uint32) {...}
}
contract Contract2 {
Contract1 public contract1 = new Contract1();
function func() public returns (uint32, uint32) {...}
}
Then you can call Contract1.func
from Contract2.func
as follows:
function func() public returns (uint32, uint32) {
uint32[2] memory ret;
address dest = address(contract1);
bytes4 selector = contract1.func.selector;
// Or bytes4 selector = bytes4(uint256(keccak256("func(uint256,uint8)") >> 224));
bytes memory data = abi.encodeWithSelector(selector, uint256(789), uint8(123));
assembly {
let success := call(
gas, // pass the remaining gas to the function
dest, // the address of the contract1 instance
0, // pass 0 wei to the function
add(data, 32), // the actual data starts after the length of 'data'
mload(data), // the length of 'data' appears at the first 32 bytes
ret, // the address of the output
8 // the size of the output
)
if iszero(success) {
revert(0, 0)
}
}
return (ret[0], ret[1]);
}
If the invoked function is constant (either pure
or view
), then you can also use staticcall
.
The example above is in assembly, but you can also use call
directly in solidity.
I think that staticcall
should be available starting from solidity v0.5.0.
There is no way to directly call an internal function through function selector.
The entry point of a contract is basically a jump table with additional checks, looking for a matching function selector between the first 4 bytes of calldata and the exposed functions of the contract.
Marking your function internal excludes it from that jump table, making it inaccessible from an external call.
Take the following contract :
contract SelectorsPublic {
bool public called;
function otherFunc(bool _called) public {
called =_called;
}
}
I deployed it here. You can check the decompiled bytecode here.
With an explicit check for the function selector in the main function body (As a reminder: keccak256("otherFunc(bool)")
== 0x64feed76...
) :
else if (var0 == 0x64feed76) {
// Dispatch table entry for 0x64feed76 (unknown)
var1 = 0x0073;
var2 = 0x006e;
var4 = 0x04;
var3 = var4 + (msg.data.length - var4);
var2 = func_0109(var3, var4);
func_006E(var2);
stop();
}
By opposition, this contract (with internal visibility) :
contract SelectorsInternal {
bool public called;
function otherFunc(bool _called) internal {
called =_called;
}
}
Deployed here, and decompiled here presents no such check. Meaning that there is no path leading to otherFunc
from the main entry point of the smart contract.
Only internal calls to it will be allowed, external ones will not find a matching entry and therefore revert (or won't even compile like in your example).
EDIT : If from the decompiled bytecode you are wondering what the check for 0x50f9b6cd
are : those are just the same principle applied to the auto generated getter for : bool public called
, keccak256("called()")
= 0x50f9b6cd....
EDIT 2:
Only external (or public) functions have the following members:
.address
: returns the address of the contract of the function.
.selector
: returns the ABI function selector
So by using either of these, you are calling the functions "externally".
Best Answer
1. Using an existing selector database
And enter your selector into the search bar. You'll get a list of known selectors that the database has found. The pros here are that it's quick, the cons are that they might not have your specific selector in the database.
2. Use Brownie, Foundry, Hardhat etc tools
Foundry
If you have the contract or interface in your foundry project you can do:
And get an output like:
And simply match up the selector (Easier with
forge inspect CallAnything methods | grep a9059cbb
)Or vice versa
And a quick way to get a selector from a function signature is to use cast from the foundry ecosystem.
Which would get you
0x0f52d66e
Brownie
You can write a script like:
and then run it:
and get an output like:
This has the added benefit of giving you what you want with the calldata from a proxy as well, and telling you what functions it calls on other contracts.
3. Pop it into Tenderly
You can go to Tenderly and just input the transaction if the transaction exists, and it will give you all the function calls the tx made to other contracts.
Or, if you haven't made the transaction, you can use the transaction simulator.
4. Write a script that converts all the functions to function selectors yourself
But who is doing that?