0

How can I get the creator's address of a contract? I'm using truffle, so usually I do

let instance = await VM.instance()

and then get the contract's address via

instance.address

Then, how can I get the creator? (Setting the owner via a constructor as in an Ownable contract is not an option)

1
  • 2
    You have to find the transaction which created the contract and then check who sent the transaction. Hopefully someone else can help you with the code for that. Commented Jan 8, 2019 at 9:28

3 Answers 3

4

This code snippet should work in general, with the only input being the contract address. live demo

I've slightly forked the code from that gist to be more robust. You'll need a provider for the web3 module, here we use alchemy but in there are many possible providers for on-chain queries like this. Relevant web3 docs

import Web3 from "web3";

// Here we use alchemy but you can use others http or websocket
const web3 = new Web3("https://eth-mainnet.alchemyapi.io/v2/<YourAPIKey>")

async function search_contract_cretion_block(contract_address) {
    var highest_block = await web3.eth.getBlockNumber();
    var lowest_block = 0;

    var contract_code = await web3.eth.getCode(contract_address, highest_block);
    if (contract_code == "0x") {
        console.error("Contract " + contract_address + " does not exist!");
        return -1;
    }

    while (lowest_block <= highest_block) {
        let search_block = parseInt((lowest_block + highest_block) / 2)
        contract_code = await web3.eth.getCode(contract_address, search_block);

        //console.log(highest_block, lowest_block, search_block, contract_code);

        if (contract_code != "0x") {
            highest_block = search_block;
        } else if (contract_code == "0x") {
            lowest_block = search_block;
        }

        if (highest_block == lowest_block + 1) {
            return highest_block;
        }
    }

}

async function search_contract_creator (contract_address, block) {
    var block = await web3.eth.getBlock(block);

    var transactions = block.transactions;

    for (let transaction in transactions) {
        let receipt = await web3.eth.getTransactionReceipt(transactions[transaction]);
        if (receipt.contractAddress?.toLowerCase() == contract_address.toLowerCase()) {
            return receipt.from
        }
    }

    return -1;
}

async function find_contract_creator (contract_address) {
    var block = await search_contract_cretion_block(contract_address);
    var creator = await search_contract_creator(contract_address, block);
    console.log(creator)
    return creator;
}

find_contract_creator("0x40e85735ff7c7230177589b4e52295b899ccc23e");

Basically, we do a binary search to find the first instance of a block which contains the contract, and then we search that block for the transaction which created that contract.

EDIT: It seems like this process does not work if the contract was created by another contract call. Not quite sure what to check for in that case...

6
  • Your example page doesn't seem to work. With the address you prefilled there, it will process for a few seconds and then stay on block 1 and address "Searching..." forever, with this error in the console: Uncaught (in promise) Error: Invalid JSON RPC response: {"id":26,"jsonrpc":"2.0"}
    – CherryDT
    Commented Aug 22, 2022 at 10:52
  • I think you need to have metamask installed to act as an ethereum provider. It works for me. Commented Aug 23, 2022 at 13:47
  • I do have MetaMask.
    – CherryDT
    Commented Aug 23, 2022 at 13:55
  • When I debug it, I can see the error the server returned is actually invalid argument 0: json: cannot unmarshal hex string of odd length into Go value of type common.Hash because something is sending 0x1 instead of 0x01 as block number. Weird
    – CherryDT
    Commented Aug 23, 2022 at 14:11
  • ...ok when I fix this manually for testing, it then fails with invalid argument 0: json: cannot unmarshal hex string of odd length into Go value of type common.Hash, because eth_getBlockByHash gets called with a block height instead of a block hash... And I notice the reason why we get down to block 1 in the first place is because eth_getCode always gets called with 'latest' and not the actual block height you are searching. Maybe it's an old web3 version? I'm confused.
    – CherryDT
    Commented Aug 23, 2022 at 14:19
0

First of all you'll need the transaction hash of the contract creation transaction. Then you can use ethereum JSON RPC to call the node and get the information of the transaction.

For the node you can use Infura or any other you like

axios.post(NODE,           
      {
        "jsonrpc":"2.0",
        "method":"eth_getTransactionByHash",
        "params":["0x3080a77bb681dd53d6b496120445a6624b0079f944b5c1a3038744596aa2228e"],
        "id":1
      }).then(res => {
           console.log(res.result.from)
 })

The response will be enter image description here OR If using web3js you can use

web3.eth.getTransaction(transactionHash [, callback])

It will return an object

{
    "hash": "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b",
    "nonce": 2,
    "blockHash": "0xef95f2f1ed3ca60b048b4bf67cde2195961e0bba6f70bcbea9a2c4e133e34b46",
    "blockNumber": 3,
    "transactionIndex": 0,
    "from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
    "to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
    "value": '123450000000000000',
    "gas": 314159,
    "gasPrice": '2000000000000',
    "input": "0x57cb2fc4"
}

from which you can take out the from key

0

Found a more reliable/faster solution than accepted answer.

If you want a contract's creator-wallet-address and you don't already know the creation TX. Best and easiest seems to be the etherscan-API.

// Note: Snippet requires npm lib node-fetch if using nodejs

// You can get an API key on the free-tier of etherscan
const APIKEY="YourApiKey" 

async function getContractCreators(
  // Array of contract-address strings (Maximum 5 per API call)
  contracts
){
  const url = `https://api.etherscan.io/api?module=contract&action=getcontractcreation&apiKey=${APIKEY}&contractaddresses=${contracts.toString()}`;
  const req = await fetch(url);
  const res = await req.json()
  // Make sure req was success
  if(res.status!='1'){
    console.warn("Handle req error here",res)
    return []
  }
  res.result.forEach(c => {
    let contract = c.contractAddress; // contract-address
    let creator  = c. contractCreator; // creator-wallet-address
    console.log("🔎 Found Creator"
    console.log("  -> contract :" + contract);
    console.log("  -> creator :" + creator)
  })
  
  return res.result
}


getContractCreators(["0xB83c27805aAcA5C7082eB45C868d955Cf04C337F","0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45","0xe4462eb568E2DFbb5b0cA2D3DbB1A35C9Aa98aad","0xdAC17F958D2ee523a2206206994597C13D831ec7","0xf5b969064b91869fBF676ecAbcCd1c5563F591d0"]);

Both ethereum and polygonscan seem to support this type of contract-creator lookup and likely other evm L2s do too.

In addition to being a magnitude faster than the accepted solution (which I also tried). This solution is much more reliable and gets ContractCreator addresses for many contracts the JSON-RPC method fails to find.

Misc advanced usage notes:

  1. This API endpoint always seems to return 200 json. Any status from the API other than '1' is a failure (ex: malformed req/rate-limit etc). In this circumstance, a string about failure is put on the result property of response json.
  2. If a creatorAddress cannot be found like in the event you request a contract-address that does not exist. Then that contractAddress will simply be omitted from the result.

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.