Web3.py – How to Call Public Variables in Python

web3.py

I have a contract that creates other contracts. It then pushes these contracts in an array, address[] public newContracts. The only problem is I'm having trouble calling them from python.

factory.json is my abi and the address and web3 seem to connect fine, just can't call the variables correctly.

Thanks for any help

from web3 import Web3
import json
from web3.providers.rpc import HTTPProvider
contractAddress = '0x3c7bec02bd4fa73dce24413d2a13c02e1a91e858'
web3 = Web3(HTTPProvider('https://ropsten.infura.io'))
with open('factory.json', 'r') as abi_definition:
    abi = json.load(abi_definition)
print (web3.eth.blockNumber)


fContract = web3.eth.contract(abi,contractAddress)
print ('Creator',fContract.call().creator)
print ('Contracts',fContract.call().newContracts)
print ('OracleName',fContract.call().oracleName)

Best Answer

Two Issues

  1. Retrieving the creator value

    print ('Creator',fContract.call().creator)

    Use fContract.call().creator() here.

  2. Creating the contract

    fContract = web3.eth.contract(abi,contractAddress)

    Use web3.eth.contract(contractAddress, abi=abi) here.

Why?

  1. Any time you access data in a contract from outside the EVM, you access it using a function. This is true even when you are accessing a public variable. Take a look at the ABI, which sets {"type":"function"} on your public variable.

  2. The arguments (abi, contractAddress) will work in Web3.py v3, but break in v4. The reversed order and abi as a keyword argument will work in both v3 and v4, so it's best to always use the newer syntax: (contractAddress, abi=abi) See the API docs for more.

Where can I read more?

Here are some lightly edited examples from the web3.py Contract call docs, using the newest Web3.py v4 syntax:

# example
In [1]: my_contract.functions.multiply7(3).call()
Out[1]: 21

In [2]: token_contract.functions.myBalance().call({'from': web3.eth.coinbase})
Out[2]: 12345
# the token balance for `web3.eth.coinbase`

The docs also discuss a slightly more succinct access, with a bit more setup. An example for your contract looks like:

from web3.contract import ConciseContract
reader = ConciseContract(fContract)

assert reader.creator()      == fContract.functions.creator().call()
assert reader.newContracts() == fContract.functions.newContracts().call()
assert reader.oracleName()   == fContract.functions.oracleName().call()