6

Suppose a contract has been deployed and I don't have its source code or ABI. From web3, I would like to know if the contract has a method with a given signature. (An example application would be a blockchain service that responds to requests by invoking a specific callback method on the calling contract, like Oraclize.)

I can get close with the following:

methodSignature = web3.sha3('methodName(bytes32,bytes)');
calltx = {
  to: TARGET_ADDR,
  data: methodSignature.substring(0,10)
};
let result = web3.eth.call(calltx)

and then if result is not '0x' then the method exists because it returned some value. But '0x' doesn't mean the method doesn't exist, depending on the method itself. How do I tell the difference?

Related: What happens if you call a non-existent function of another contract that has no fallback function in Solidity?

7 Answers 7

11

I'm using this method:

public async hasMethod(contractAddress, signature) {
    const w3 = this.$connection.web3; // this is my web3 instance
    const code = await w3.eth.getCode(contractAddress);
    const functionSignature = w3.eth.abi.encodeFunctionSignature(signature);
    // remove "0x" prefixed in 0x<4bytes-selector>
    return code.indexOf(functionSignature.slice(2, hash.length)) > 0;
  }

And than:

const adress = '0xB8c77482e45F1F44dE1745F52C74426C631bDD52';
const signature = 'transferFrom(address,address,uint256)';
this.hasMethod(address, signature).then((has) => console.log(has));
2
  • 1
    Very clever Anton, I like this! Sep 15, 2021 at 17:42
  • 1
    I believe this line return code.indexOf(functionSignature.slice(2, hash.length)) > 0; should be return code.indexOf(functionSignature.slice(2, functionSignature.length)) > 0;
    – Max
    Dec 2, 2022 at 14:16
4

There is no perfect way to do this, but you can scan the code for occurrences of the signature hash. To make this a bit more precise you can make sure that the opcode of PUSH4 precedes it.

Here is some python code for this (I guess JS is almost identical):

def hasMethod(contract_addr, signature):
    code = w3.eth.getCode(contract_addr)
    fn_hash = w3.sha3(signature.encode("utf-8"))
    fn_hash = "63" + fn_hash[2:10] # 0x63 is PUSH4
    return fn_hash in code

contract_addr = "0xB8c77482e45F1F44dE1745F52C74426C631bDD52"
signature = "transferFrom(address,address,uint256)"
print(hasMethod(contract_addr, signature))
0

This is not part of WEB3.js 1.0, this is a basic javascript function, that could only check if supplied ABI does have this property (method), but it will not be able to check if the smart contract instantiated with this ABI really does have this method

EVM does not support validation of method existence. Best you can do is to call the method and if the method is programmed in a way that non-zero result will always be returned if method exists, then you can validate it.

There is no way around it otherwise.

0

yes, there is a way. If the smart contract address owner have given the codes on its polyscan/etherescan, then you can get codes, they also provider API, so that you don't need to retrieve available smart contract codes manually.

otherwise, with web3.eth.getCode(address) you can only get bytecode. not function/method names or contract codes or ABI of any deployed smart contract.

if you are thinking to DeCode bytecode to source code and then get ABI, then let me tell you that: "no one can ever take out function/method names from bytecode, because when codes are compiled to bytecode, they don't store variable names"

0

one of methods is a search method in Contract code:

For realizte this its need ABI

const ABIERC721 = [
        {
            inputs: [
                {
                    internalType: "uint256",
                    name: "tokenId",
                    type: "uint256",
                },
            ],
            name: "tokenURI",
            outputs: [
                {
                    internalType: "string",
                    name: "",
                    type: "string",
                },
            ],
            stateMutability: "view",
            type: "function",
        },
    ];

/*
part of code of contract
0x608060405234801561001057600080fd5b50600436106102e25760003560e
01c806370a0823111610188578063ad2d0ddd116100e4578063d5a06d4c1161
0092578063d5a06d4c14610668578063e00aab4b146106af578063e92a89f61
46106c2578063e985e9c5146106d5578063f0cdc499146106e8578063f2fde3
8b146106fb578063fe2e1f581461070e57600080fd5b8063ad2d0ddd1461060
f578063b0fe87c914610622578063b88d4fde14610635578063b9c4d9fb1461
0648578063bb3bafd614610668578063***c87b56dd***14610689578063...
                                   --------
*/
const contractCode = await web3.eth.getCode(contractAddress);

/*
{
  tokenURI: [Function: bound _createTxObject],
  '0xc87b56dd': [Function: bound _createTxObject],
  'tokenURI(uint256)': [Function: bound _createTxObject]
}
*/
const contract = new web3.eth.Contract(ABIERC721, contractAddress);

//return array with hexed names like ['0xc87b56dd']
const getHexName = Object.keys(contract.methods).filter((item) =>
                /0x/gim.test(item)
            );
//create regexp removing '0x' and find it on contract code
const isExistMethod = new RegExp(
                getHexName.join("|").replace("0x", ""),
                "gim"
            ).test(contractCode);

Or without ABI:


const methodSha = web3.utils.sha3("tokenURI(uint256)");
const methodToCheck = methodSha.substring(2,10); //c87b56dd

const contractCode = await web3.eth.getCode(contractAddress);

//create regexp removing '0x' and find it on contract code
const isExistMethod = new RegExp(methodToCheck,"gim").test(contractCode);

Regads

0

An updated version of mafrasi2, for Python 3, more efficient, and supporting all web3py versions (including the upcoming v6):

from eth_hash.auto import keccak

def has_function(contract_addr: str, signature: str) -> bool:
    code = w3.eth.get_code(contract_addr).hex()
    fn_hash = keccak(signature.encode()).hex()
    fn_hash = f"63{fn_hash[:8]}"  # 0x63 is PUSH4
    return fn_hash in code
-1

With web3 1.0.0 you can check for the existance of the signature inside the methods property of your contract instance.

contract.methods.hasOwnProperty('SIGNATURE')
1
  • 1
    He needs the ABI for that though which he doesn't have access to.
    – mafrasi2
    Apr 7, 2018 at 23:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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