3

I have deployed a contract on Ropsten, this contract has a function name testFunc() and I want to execute testFunc() from a python script.

This is how I'm connecting to the contract from python:

from web3 import Web3

def connect_to_contract(self):
    try:
        self.web3 = Web3(
            Web3.HTTPProvider(
                "https://" +
                self.network +
                ".infura.io/v3/" +
                os.getenv("WEB3_INFURA_PROJECT_ID")))
        self.contract = self.web3.eth.contract(
            address=self.contract_address, abi=self.contract_abi)
    except Exception as e:
        raise e

Then, this is how I first tried to execute the contract function but nothing happend:

self.contract.functions.testFunc().call()

I've read on different topics that I've to sign the transaction in order to make it work. So that's how I'm trying to do know:

txn = self.contract.functions.testFunc().buildTransaction({
            'gas': 70000,
            'gasPrice': self.web3.toWei('1', 'gwei'),
            'from': self.contract_address,
            'nonce': nonce
    })
private_key = "xxxxxxxxxx" 
signed_txn = self.web3.eth.account.signTransaction(txn, private_key=private_key)
self.web3.eth.sendRawTransaction(signed_txn.rawTransaction)

But that's don't work neither and I have the error message:

TypeError: from field must match key's 0x25c77Axxxxxxxxxxxxxxxxxxxxxxxx, but it was 0x7d8e0ea7axxxxxxxxxxxxxxxxxxxx

The 0x25c77Axxxxxxxxxxxxxxxxxxxxxxxx address mentionned here is the public key I used to deploy the contract, the second one 0x7d8e0ea7axxxxxxxxxxxxxxxxxxxx is the contract which have the testFunc() function.

So my question is, how to delegate the call from the python script or how can I get the private key for the deployed contract ? How should I do ?

1 Answer 1

6

Dev advocate at Chainstack here!

When you use web3.py to interact with smart contracts, you can divide the script into three parts:

  • Connect to the network.
  • Initialize the smart contract & account to sign the transactions from.
  • Call functions and transactions.

Connect to the network

I always use this syntax to connect my scripts to a network, you can also use an environment variable, but I usually keep it simple for test scripts.

from web3 import Web3

# Initialize endpoint URL
node_url = "CHAINSTACK_NODE_URL"

# Create the node connection
web3 = Web3(Web3.HTTPProvider(node_url))

I usually add an if statement to give a message on the console to verify if the connection is successful (non-required but excellent for the user).

# Verify if the connection is successful
if web3.is_connected():
    print("-" * 50)
    print("Connection Successful")
    print("-" * 50)
else:
    print("Connection Failed")

Initialize smart contract and account

To call functions from a smart contract, we need to specify its address and ABI first.

# Initialize the address calling the functions/signing transactions
caller = "YOUR_ADDRESS"
private_key = "PRIVATE_KEY"  # To sign the transaction

# Initialize address nonce
nonce = web3.eth.get_transaction_count(caller)

# Initialize contract ABI and address
abi = '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"balanceLeft","type":"uint256"}],"name":"balance","type":"event"},{"inputs":[{"internalType":"address payable","name":"recipient","type":"address"}],"name":"destroy","outputs":[],"stateMutability":"nonpayable","type":"function"}'

contract_address = "CONTRACT_ADDRESS"

# Create smart contract instance
contract = web3.eth.contract(address=contract_address, abi=abi)

Call functions and transactions

Now we can start calling functions; we can do it two ways:

  • .call in case the function is only reading from the blockchain (if it's a view or pure function)

  • Build a transaction to call a function that modifies the state of the network.

I noticed that on your code you use the contract address to call the function instead of calling it from your account.

# initialize the chain id, we need it to build the transaction for replay protection
Chain_id = web3.eth.chain_id

# Call your function
call_function = contract.functions.testFunc().buildTransaction({"chainId": Chain_id, "from": caller, "nonce": nonce})

# Sign transaction
signed_tx = web3.eth.account.sign_transaction(call_function, private_key=private_key)

# Send transaction
send_tx = web3.eth.send_raw_transaction(signed_tx.rawTransaction)

# Wait for transaction receipt
tx_receipt = web3.eth.wait_for_transaction_receipt(send_tx)
# print(tx_receipt) # Optional

Then if you want to call a function that only reads from the network (view or pure), you only need to "call" it since it will not make an actual transaction.

For example the totalSupply() function from an ERC20 token contract. You can also read the public variables of the in this way.

totalSupply = contract.functions.totalSupply().call()  # read the coin total supply - call means we are reading from the blockchain
print(totalSupply) 

You can check the Node API reference page on the Chainstack docs to see more web3.py code examples!

I hope this helps you!

3
  • Thanks a lot @Soos3D for your help. Can you please explain me what's the difference between the caller and the contract address ?
    – rastafalow
    Aug 31, 2022 at 14:01
  • No problem at all; I'm glad I can help! The caller is the account you use to call the function and make the transaction, for example, the account you have set up on MetaMask, while the contract address is the address where the smart contract is deployed. I updated the answer to show that a bit more clearly :) Aug 31, 2022 at 16:36
  • @Soos3D Thanks! this answer was very helpful to me. Nov 17, 2022 at 14:43

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.