1

Is there a way to get a method signature from the contract object (using Web3.py) rather than manually constructing it? I don't see a function in there that accomplishes this. It seems like something should be built-in.

I have always calculated the method signature manually using Web3.sha3 as seen below, but this requires me to manually type out the function params.

params = 'uint256,uint256,bytes,address[]'
method_signature = Web3.sha3(text=f"functionNameGoesHere({params})")[0:4]

The contract object already knows the ABI JSON, so shouldn't it be able to generate it for me?

It would be nice if I could do this:

function.method_signature

Since I can already do this:

function.abi

Note, I'm NOT trying to build a transaction to broadcast to the network. I'm trying to generate lots of function hashes from contracts given the contract object an ABI. And these contracts will change over time so I don't want to hard code anything.

Edit: I wrote this myself to do the job. It supports regular types as well as tuples. Would still prefer to use something built-in though.

def GetMethodSignature_GivenAbi(methodName, abi):
    params = ''
    for index, input in enumerate(abi['inputs']):
        if index > 0:
            params += ','

        if input['type'] == 'tuple':
            params += '('
            for index2, tupleComponent in enumerate(input['components']):
                if index2 > 0:
                    params += ','

                params += tupleComponent['type']

            params += ')'

        else:
            params += input['type']

    methodSignature = GetMethodSignature(methodName, params)
    return methodSignature

ABI simple example:

{
  'constant': False,
  'inputs': [
    {
      'name': 'name1',
      'type': 'uint256'
    },
    {
      'name': 'name2',
      'type': 'uint256'
    }
  ],
  'name': 'simpleFunction',
  'outputs': [

  ],
  'payable': False,
  'stateMutability': 'nonpayable',
  'type': 'function'
}

ABI tuple example:

{
  'constant': False,
  'inputs': [
    {
      'name': 'name1',
      'type': 'uint256'
    },
    {
      'name': 'name2',
      'type': 'uint256'
    },
    {
      'name': 'name3',
      'type': 'uint256'
    },
    {
      'components': [
        {
          'name': 'makerAddress',
          'type': 'address'
        },
        {
          'name': 'takerAddress',
          'type': 'address'
        },
        {
          'name': 'feeRecipientAddress',
          'type': 'address'
        },
        {
          'name': 'senderAddress',
          'type': 'address'
        },
        {
          'name': 'makerAssetAmount',
          'type': 'uint256'
        },
        {
          'name': 'takerAssetAmount',
          'type': 'uint256'
        },
        {
          'name': 'makerFee',
          'type': 'uint256'
        },
        {
          'name': 'takerFee',
          'type': 'uint256'
        },
        {
          'name': 'expirationTimeSeconds',
          'type': 'uint256'
        },
        {
          'name': 'salt',
          'type': 'uint256'
        },
        {
          'name': 'makerAssetData',
          'type': 'bytes'
        },
        {
          'name': 'takerAssetData',
          'type': 'bytes'
        },
        {
          'name': 'makerFeeAssetData',
          'type': 'bytes'
        },
        {
          'name': 'takerFeeAssetData',
          'type': 'bytes'
        }
      ],
      'name': 'order',
      'type': 'tuple'
    },
    {
      'name': 'name4',
      'type': 'bytes'
    },
    {
      'name': 'name5',
      'type': 'address[]'
    }
  ],
  'name': 'functionName',
  'outputs': [

  ],
  'payable': False,
  'stateMutability': 'nonpayable',
  'type': 'function'
}
  • In web3.js, the signature of every function is available in the contract object immediately after you create it (no need to deploy, no need to interact with a node). Perhaps it's the same on web3.py. Why don't you check the object you get after creating the contract? – goodvibration Mar 24 at 19:41
  • I didn't see anything helpful in there. I looked at the properties and attributes available of the contract object. There are calls to get functions(aka methods), and from there you can get the abi of the function. But I didn't see anything about getting a hash or signature of the method. – LampShade Mar 25 at 1:29
  • However... you can use those abis (of each function in the contract). And that is sufficient for achieving your primary goal, which is to avoid hard coding them in your calls to Web3.sha3. – goodvibration Mar 25 at 8:04
  • Yep exactly. And that works just fine to achieve the primary goal. It just feels like if the logic is in web3 that I shouldn't have to re-implement the logic. – LampShade Mar 25 at 14:13
1

A contract on a blockchain does not store method signatures, only hashes of method signatures.

This information is encoded in the EVM bytecode as "jump destinations" based on the method signature.

For everything else, you need ABI files, precompiled or compile yourself.

EtherScan collects contract source code and recompiles it when you "verify" the contract.

| improve this answer | |
  • Sounds like a misunderstanding. I'm not expecting the method hash to be available in solidity. This is from the perspective of using Web3.py, so python code. And this assumes I have the contract address and contract ABI. I'm trying to automate calculating the method signature using web3.py, rather than manually iterating over the ABI inputs (which may contain a tuple components and get pretty long) – LampShade Mar 24 at 19:16
  • Ah, sorry for misunderstanding! I think with Web3.py, there is a method to get the signature straight from the Contract object, as a helper method, without trying to calculate yourself. However, as far as I remember, it was not publicly documented API and you might need dig into web3.Contract with a debugger and a bit to discover it. To get there you first need to make proper Contract objects out from your ABI files. – Mikko Ohtamaa Mar 24 at 21:47
  • I have made proper contract and function objects. I dug through the attributes and properties in the function object and didn't see anything there that was useful. I'll take another look at the contract object. I ended up writing the function myself to what I need. I'll post it above. If you ever come across this in web3 let me know! – LampShade Mar 25 at 1:23
  • I checked last night. There is abi_to_signature utility function, but ContractFunctions API does not offer this - so looks you need to generate hashes yourself. – Mikko Ohtamaa Mar 25 at 9:11
0

Given the ABI of a contract, you can generate the signature of every function in that contract:

signatures = {}
for func in [obj for obj in abi if obj['type'] == 'function']:
    name = func['name']
    types = [input['type'] for input in func['inputs']]
    signatures[name] = '{}({})'.format(name,','.join(types))

And then call Web3.sha3 with each one of these signatures.

This should allow you to handle contract changes programmatically.


If it makes it easier for you, then you can also create an array instead of a dictionary:

signatures = ['{}({})'.format(func['name'],','.join([input['type'] for input in func['inputs']])) for func in [obj for obj in abi if obj['type'] == 'function']]
| improve this answer | |
  • Thanks. I was trying to avoid this logic entirely seeing as it's already being done in web3 under the hood. I was hoping web3 has something exposed since they're doing it already anyways for their call that builds a transaction from a function name. I did write the logic to do this myself, I'd just rather something built in. One thing you could add to yours is support for tuples, you have to check for "components" as a type. – LampShade Mar 25 at 12:58
  • @LampShade: It's a 1-liner logic (or 5-liner if you choose the first option suggested above). What better solution were you hoping to find via Web3? I mean, you'd still have to call that Web3 function if it was provided. That would also take at least one line of code, right? I did not understand your mentioning about "support for tuples". Functions can take any number of arguments, and function signatures naturally abide to that. How exactly does "tuples" fit into all of this? – goodvibration Mar 25 at 13:02
  • The 0xv2 and 0xv3 protocol in Ethereum uses a lot of tuples to enable them to support classes in solidity. I'll update the OP above with examples of ABI tuple vs non-tuple. – LampShade Mar 25 at 14:17
0

I don't know if I understood correctly, You can get a function selector for a function in a smart contract like this.

Greeter = w3.eth.contract(abi=abi, bytecode=bytecode)
Greeter.functions.getTime().selector
>>>'0x557ed1ba'

Where getTime() is a public function of Smart contract.

if a function accepts arguments,then

Web3.sha3(text=f"setValue({'uint256'})")[0:4]
>>>HexBytes('0x55241077')

Greeter.functions.setValue(1364283634).selector
>>>'0x55241077'

You can also call the selector function on a deployed instance of a contract using its abi and address.

| improve this answer | |
  • Thanks, This seems to work! But when I use Greeter.functions.getTime().selector, for example, it's expecting me to pass in arguments for the function. For example, if getTime expected as parameters an address and a uint256, I'd have to pass them in. I'd be nice to dumbly not have to pass anything in. The point of me doing this is to fully automate this as simply as possible. Having to generate arguments to put in there just so it spits out the hash would be a bit annoying for my specific use case. – LampShade Apr 4 at 21:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.