Web3.py – How Can the Transaction Hash / Block Hash of an Already Deployed Contract Be Retrieved?

blockstransactionsweb3.py

If you use Web3.py to deploy a Solidity contract to the blockchain, you can retrieve a transaction hash, like this:

tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction)

Once you have that tx_hash, you can retrieve a transaction receipt, like this:

tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)

However, I haven't been able to find a straightforward way to retrieve that information for a contract that I didn't deploy (or that I deployed at some point in the past and didn't record).

Couple of notes:

  • There could be multiple transactions across multiple blocks for that contract.
  • I'm looking for a solution using Web3 — I'd prefer not to traverse the entire blockchain or go to Etherscan and look up the information.

I would like to get an array of either:

  • all the block numbers that contain a transaction for a given contract
  • all the transaction receipts for a given contract

after the contract has been deployed. Is that possible?

For example, let's imagine that I wanted to find all of the transaction receipts for a smart contract deployed on the Ropsten test network.

Let's use the helloWorld contract deployed at this address: 0xb1Afb360F9ba99883166236a4b2DdAa9e3ff397a

This contract has been verified on Etherscan, so its ABI is available. You can see it on Etherscan here: https://ropsten.etherscan.io/address/0xb1afb360f9ba99883166236a4b2ddaa9e3ff397a#code

The following Python code will allow you to instantiate the contract, and call functions defined in the ABI:

abi = '''
[{"constant":false,"inputs":[{"name":"_wordsToSay","type":"string"}],"name":"sayAnything","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"sayHello","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]
'''

contract_address = "0xb1Afb360F9ba99883166236a4b2DdAa9e3ff397a"
contract = w3.eth.contract(address=contract_address, abi=abi)

pprint.pprint(contract.functions.sayHello().call())

The only other attributes I have been able to retrieve are the address and the ABI.

Is there any way to retrieve the block numbers or transaction hashes for an already deployed contract?

Best Answer

Since you're interested in arbitrary contracts that don't emit events, you'll have to scan each block. Additionally, to capture scenarios where the contract you're interested in is called by another contract (instead of an Externally Owned Account), you'll likely have to run a trace on each transaction.

Traces are non-standard (client-specific) mechanisms for peeking inside the EVM mechanics of a transaction. Infura does not support traces, AFAIK. So you'll almost certainly have to run a local node.

Web3.py only supports parity traces, currently, so I'd probably start there. Unfortunately they're not documented in web3.py docs at the moment. You would roughly do something like:

from web3.auto import w3
from web3.parity import Parity
Parity.attach(w3, 'parity)
trace_results = w3.parity.traceReplayBlockTransactions('latest')

You can find more parity trace methods in the web3py source.

Related Topic