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.
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:
forge inspect CallAnything methods
And get an output like:
{
"approve(address,uint256)": "095ea7b3",
"balanceOf(address)": "70a08231",
"transfer(address,uint256)": "a9059cbb"
}
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.
cast sig "addTwo(uint256,uint256)"
Which would get you 0x0f52d66e
Brownie
You can write a script like:
# call_me.py
from brownie import Contract
def main():
calldata = "some_calldata_here"
result = Contract.from_explorer(
"0x2a49Eae5CCa3f050eBEC729Cf90CC910fADAf7A2"
).decode_input(calldata)
print(result)
and then run it:
brownie run scripts/call_me.py --network mainnet
and get an output like:
Fetching source of 0x2a49Eae5CCa3f050eBEC729Cf90CC910fADAf7A2 from api.etherscan.io...
('openMultiplyVault((address,address,uint256,uint256,uint256,address,bytes),(address,address,uint256,bytes32,uint256,uint256,uint256,uint256,uint256,uint256,bool,string),(address,address,address,address,address))', [['0x6B175474E89094C44Da98b954EedeAC495271d0F', .........
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?
Best Answer
Here are the step by steps from the metamask documentation:
Go to the Mainnet Parity signature registration contract on etherscan
Connect MetaMask
Use etherscan's write contract feature to input the string value (without quotes or spaces) to the register function (0x59708645)
For example:
getOwners()
execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)
Click
"write"
Approve the transaction in MetaMask (you only pay gas)