Delegatecall – How to Find the Function in a Contract from its Selector Quickly and Efficiently

decodingdelegatecalldsproxyfunction-selectorproxy-contracts

Let's say I have a function selector:

0xa9059cbb

And I have a contract as such:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract CallAnything {
  function balanceOf(address someAddress) external view {...}
  function transfer(address someAddress, uint256 amount) external {...}
  function approve(address someAddress, uint256 amount) external {...}
}

What's the quickest way for me to find which function the selector involves?

More context

Many proxies use delegatecall to call some contract with a target address and just the data object. If I'm a user interacting with a proxy from a UI/front end, it can be really tricky to see exactly what function and with what data my proxy is calling.

A way to quickly find the:

  1. Function being called
  2. (not in the scope of this question) decode the parameters being sent.

Would be lovely.

Hint, in the above contract, the function selector 0xa9059cbb is for the transfer function. It has a function signature of "transfer(address,uint256)"

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:

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?

Related Topic